mirror of
https://github.com/Telecominfraproject/wlan-cloud-analytics.git
synced 2026-03-20 03:39:59 +00:00
Compare commits
179 Commits
v2.6.0-RC1
...
v2.8.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b8f2471d2 | ||
|
|
b14cc814e4 | ||
|
|
cdf71c2c99 | ||
|
|
56592fc350 | ||
|
|
ee0123fae5 | ||
|
|
28ccd71880 | ||
|
|
bbc83cd0a1 | ||
|
|
d040bb672d | ||
|
|
96d133c511 | ||
|
|
af3a655ece | ||
|
|
a80ddb727a | ||
|
|
c3cd76333a | ||
|
|
3f1887eeb3 | ||
|
|
c57419b4e1 | ||
|
|
e94d6e26b9 | ||
|
|
1348be3011 | ||
|
|
24da84e6ea | ||
|
|
2a17655116 | ||
|
|
53e7288c07 | ||
|
|
d9677884a2 | ||
|
|
b0faa44582 | ||
|
|
04841cf0d4 | ||
|
|
f8f3390342 | ||
|
|
25df7e5f5c | ||
|
|
d8d6168f49 | ||
|
|
5d5f66f4c5 | ||
|
|
ce99365f0c | ||
|
|
2f2a94ed5c | ||
|
|
2bd5f9139b | ||
|
|
6254936142 | ||
|
|
f52aef80cb | ||
|
|
12f003bb09 | ||
|
|
a1f0fafed1 | ||
|
|
ff084ec00c | ||
|
|
6af116d518 | ||
|
|
6918f97f93 | ||
|
|
3cfac6cf16 | ||
|
|
c6def7e0e1 | ||
|
|
55bea4e8ad | ||
|
|
0314746764 | ||
|
|
4d5bab043d | ||
|
|
90d10c06e2 | ||
|
|
5780e2585f | ||
|
|
fa85884d97 | ||
|
|
3cf7d2f8d6 | ||
|
|
110c88b760 | ||
|
|
427b4be808 | ||
|
|
1041595911 | ||
|
|
1712b2834f | ||
|
|
2786a3177c | ||
|
|
776745f597 | ||
|
|
bb0310d34f | ||
|
|
6f465479f3 | ||
|
|
6990860e61 | ||
|
|
4e6b92cadf | ||
|
|
d99ee187f7 | ||
|
|
40c6d757a7 | ||
|
|
59b00f3429 | ||
|
|
c7c91360a2 | ||
|
|
8e148a6d33 | ||
|
|
4d44884686 | ||
|
|
e3467ef3ad | ||
|
|
b9128f1b50 | ||
|
|
da80ab49cd | ||
|
|
bf0fd21eae | ||
|
|
829efdfec8 | ||
|
|
d258bbb414 | ||
|
|
972bbbafdc | ||
|
|
f0442d4c8e | ||
|
|
f3713df910 | ||
|
|
085fbfcdf3 | ||
|
|
e7997f28f4 | ||
|
|
ef7deb3515 | ||
|
|
70444bb7ab | ||
|
|
105bf2912b | ||
|
|
87dfb313d5 | ||
|
|
5658db43eb | ||
|
|
bff4db6a3a | ||
|
|
8ecc461749 | ||
|
|
97377fef7b | ||
|
|
877f7985cd | ||
|
|
9b916621a2 | ||
|
|
5e57c9a7c5 | ||
|
|
e9bb037ef2 | ||
|
|
054c40c7ed | ||
|
|
99c59313ea | ||
|
|
ff843f093c | ||
|
|
eec96dff52 | ||
|
|
9c9f2cbf4f | ||
|
|
98342bb626 | ||
|
|
93890a1e99 | ||
|
|
f9111bab7b | ||
|
|
8f9e954f20 | ||
|
|
a568fce901 | ||
|
|
6e2a21c831 | ||
|
|
da8a9e14cf | ||
|
|
075b83a7d5 | ||
|
|
3c35ec1523 | ||
|
|
b65f33c975 | ||
|
|
cbd5b09cdf | ||
|
|
0a145c8501 | ||
|
|
2ed88569d0 | ||
|
|
58c2269893 | ||
|
|
a708cc2be0 | ||
|
|
9c8f7ed6f9 | ||
|
|
b79a3b6c12 | ||
|
|
2c3b7711b3 | ||
|
|
831e144869 | ||
|
|
67910c982a | ||
|
|
9be196ce40 | ||
|
|
222afe2858 | ||
|
|
40a02c4752 | ||
|
|
7f1577503f | ||
|
|
7ab2e6c96e | ||
|
|
639db8631e | ||
|
|
9fe9b74dea | ||
|
|
20c025f994 | ||
|
|
ee78155b68 | ||
|
|
9f9a866ad3 | ||
|
|
adbd852ccd | ||
|
|
4bb5ee0b86 | ||
|
|
06bb8cebbe | ||
|
|
dde46ce6b2 | ||
|
|
515f8a4fea | ||
|
|
8dcfe04310 | ||
|
|
5db6fc3027 | ||
|
|
25397c80c9 | ||
|
|
2e046340e3 | ||
|
|
61dff87583 | ||
|
|
c73fd21b94 | ||
|
|
8049291138 | ||
|
|
613fa2e2bb | ||
|
|
eb9a748836 | ||
|
|
6ee3cae1db | ||
|
|
33e4f8abd8 | ||
|
|
d5b4d15307 | ||
|
|
e01f985974 | ||
|
|
8dc3e53fc0 | ||
|
|
52e1bf8817 | ||
|
|
a8b0028af6 | ||
|
|
7976602d17 | ||
|
|
55b0075c0c | ||
|
|
1da468e52c | ||
|
|
fa434f79c3 | ||
|
|
d396a104ff | ||
|
|
61837a8975 | ||
|
|
c1f06ec784 | ||
|
|
10ffba95b5 | ||
|
|
fd3a6be11a | ||
|
|
ec2874748f | ||
|
|
432d0ed52b | ||
|
|
fe247c6093 | ||
|
|
4c1daf84cf | ||
|
|
1d4aa80205 | ||
|
|
d0c4030289 | ||
|
|
34100b5fb0 | ||
|
|
0856e256e9 | ||
|
|
228b2162bc | ||
|
|
d4602f0db1 | ||
|
|
d4148e6f01 | ||
|
|
d18d567166 | ||
|
|
c8186b30b7 | ||
|
|
d83c841455 | ||
|
|
c180e60d0a | ||
|
|
dff48d6561 | ||
|
|
052c685938 | ||
|
|
ee916d5db6 | ||
|
|
64799106ae | ||
|
|
643e748a95 | ||
|
|
c36f222416 | ||
|
|
9e9b7cc4e8 | ||
|
|
6c81f0585c | ||
|
|
76d6abc994 | ||
|
|
e905fbe155 | ||
|
|
5c778c0de5 | ||
|
|
d1a01e9d31 | ||
|
|
0ceb204a83 | ||
|
|
df7c860246 | ||
|
|
1ab0e7f291 |
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
DOCKER_REGISTRY_USERNAME: ucentral
|
||||
steps:
|
||||
- name: Checkout actions repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Telecominfraproject/.github
|
||||
path: github
|
||||
@@ -57,11 +57,11 @@ jobs:
|
||||
- name: Get base branch name and set as output
|
||||
id: get_base_branch
|
||||
run: |
|
||||
echo ::set-output name=branch::$(echo ${GITHUB_BASE_REF##*/})
|
||||
echo ::set-output name=owgw_branch::$(echo ${GITHUB_BASE_REF##*/} | sed 's/main/master/g')
|
||||
echo "branch=$(echo ${GITHUB_BASE_REF##*/})" >> $GITHUB_OUTPUT
|
||||
echo "owgw_branch=$(echo ${GITHUB_BASE_REF##*/} | sed 's/main/master/g')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Checkout actions repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Telecominfraproject/.github
|
||||
path: github
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
- docker
|
||||
steps:
|
||||
- name: Checkout actions repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Telecominfraproject/.github
|
||||
path: github
|
||||
|
||||
9
.github/workflows/cleanup.yml
vendored
9
.github/workflows/cleanup.yml
vendored
@@ -4,6 +4,7 @@ on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'release/*'
|
||||
types: [ closed ]
|
||||
|
||||
defaults:
|
||||
@@ -16,4 +17,10 @@ jobs:
|
||||
steps:
|
||||
- run: |
|
||||
export PR_BRANCH_TAG=$(echo ${GITHUB_HEAD_REF#refs/heads/} | tr '/' '-')
|
||||
curl -uucentral:${{ secrets.DOCKER_REGISTRY_PASSWORD }} -X DELETE "https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral/owanalytics/$PR_BRANCH_TAG"
|
||||
|
||||
if [[ ! $PR_BRANCH_TAG =~ (main|master|release-*) ]]; then
|
||||
echo "PR branch is $PR_BRANCH_TAG, deleting Docker image"
|
||||
curl -s -uucentral:${{ secrets.DOCKER_REGISTRY_PASSWORD }} -X DELETE "https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral/owanalytics/$PR_BRANCH_TAG"
|
||||
else
|
||||
echo "PR branch is $PR_BRANCH_TAG, not deleting Docker image"
|
||||
fi
|
||||
|
||||
2
.github/workflows/enforce-jira-issue-key.yml
vendored
2
.github/workflows/enforce-jira-issue-key.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout actions repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
repository: Telecominfraproject/.github
|
||||
path: github
|
||||
|
||||
41
.github/workflows/openapi-pages.yml
vendored
Normal file
41
.github/workflows/openapi-pages.yml
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
name: Update OpenAPI docs on GitHub Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'openapi/**'
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
docsgen:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Generate static HTML page with docs from OpenAPI definition
|
||||
run: |
|
||||
docker run --rm -v "${PWD}:/local" openapitools/openapi-generator-cli:v6.2.1 generate -i https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-analytics/main/openapi/owanalytics.yaml -g html2 --skip-validate-spec -o /local/
|
||||
|
||||
- name: Update OpenAPI docs
|
||||
run: |
|
||||
mkdir tmp-docs
|
||||
mv index.html tmp-docs/index.html
|
||||
mkdir -p ~/.ssh
|
||||
ssh-keyscan -H github.com >> ~/.ssh/known_hosts
|
||||
echo https://tip-automation:${{ secrets.GIT_PUSH_PAT }}@github.com > ~/.git-credentials
|
||||
git config --global credential.helper store
|
||||
git config --global user.email "tip-automation@telecominfraproject.com"
|
||||
git config --global user.name "TIP Automation User"
|
||||
git pull
|
||||
git checkout gh-pages || git checkout -b gh-pages
|
||||
rm -rf docs
|
||||
mv tmp-docs docs
|
||||
git add docs
|
||||
git commit -m'Update OpenAPI docs for GitHub pages'
|
||||
git push --set-upstream origin gh-pages
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
||||
HELM_REPO_USERNAME: ucentral
|
||||
steps:
|
||||
- name: Checkout uCentral assembly chart repo
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
path: wlan-cloud-analytics
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -32,6 +32,7 @@
|
||||
*.app
|
||||
|
||||
test_scripts/curl/token.json
|
||||
test_scripts/curl/result.json
|
||||
.vscode/c_cpp_properties.json
|
||||
test_scripts/curl/result.json
|
||||
*.swp
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
cmake_minimum_required(VERSION 3.13)
|
||||
project(owanalytics VERSION 2.6.0)
|
||||
project(owanalytics VERSION 2.8.0)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_GENERATOR "Unix Makefiles")
|
||||
@@ -28,17 +28,18 @@ endif()
|
||||
|
||||
find_package(Git QUIET)
|
||||
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --tags
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
RESULT_VARIABLE GIT_RESULT
|
||||
OUTPUT_VARIABLE GIT_HASH)
|
||||
if(NOT GIT_RESULT EQUAL "0")
|
||||
message(FATAL_ERROR "git describe --always --tags failed with ${GIT_RESULT}")
|
||||
message(FATAL_ERROR "git rev-parse --short HEAD failed with ${GIT_RESULT}")
|
||||
endif()
|
||||
string(REGEX REPLACE "\n$" "" GIT_HASH "${GIT_HASH}")
|
||||
endif()
|
||||
|
||||
find_package(OpenSSL REQUIRED)
|
||||
find_package(ZLIB REQUIRED)
|
||||
find_package(Poco REQUIRED COMPONENTS Crypto JWT Net Util NetSSL Data DataSQLite)
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
find_package(nlohmann_json_schema_validator REQUIRED)
|
||||
@@ -63,6 +64,8 @@ include_directories(/usr/local/include /usr/local/opt/openssl/include src inclu
|
||||
|
||||
configure_file(src/ow_version.h.in ${PROJECT_SOURCE_DIR}/src/ow_version.h @ONLY)
|
||||
|
||||
add_definitions(-DPOCO_LOG_DEBUG="1")
|
||||
|
||||
add_executable(owanalytics
|
||||
build
|
||||
src/ow_version.h.in
|
||||
@@ -72,14 +75,56 @@ add_executable(owanalytics
|
||||
src/framework/OpenWifiTypes.h
|
||||
src/framework/orm.h
|
||||
src/framework/StorageClass.h
|
||||
src/framework/MicroServiceErrorHandler.h
|
||||
src/framework/UI_WebSocketClientServer.cpp
|
||||
src/framework/UI_WebSocketClientServer.h
|
||||
src/framework/UI_WebSocketClientNotifications.cpp
|
||||
src/framework/UI_WebSocketClientNotifications.h
|
||||
src/framework/utils.h
|
||||
src/framework/utils.cpp
|
||||
src/framework/AppServiceRegistry.h
|
||||
src/framework/SubSystemServer.cpp
|
||||
src/framework/SubSystemServer.h
|
||||
src/framework/RESTAPI_utils.h
|
||||
src/framework/AuthClient.cpp
|
||||
src/framework/AuthClient.h
|
||||
src/framework/MicroServiceNames.h
|
||||
src/framework/MicroServiceFuncs.h
|
||||
src/framework/OpenAPIRequests.cpp
|
||||
src/framework/OpenAPIRequests.h
|
||||
src/framework/MicroServiceFuncs.cpp
|
||||
src/framework/ALBserver.cpp
|
||||
src/framework/ALBserver.h
|
||||
src/framework/KafkaManager.cpp
|
||||
src/framework/KafkaManager.h
|
||||
src/framework/RESTAPI_RateLimiter.h
|
||||
src/framework/WebSocketLogger.h
|
||||
src/framework/RESTAPI_GenericServerAccounting.h
|
||||
src/framework/RESTAPI_SystemConfiguration.h
|
||||
src/framework/CIDR.h
|
||||
src/framework/RESTAPI_Handler.cpp
|
||||
src/framework/RESTAPI_Handler.h
|
||||
src/framework/RESTAPI_ExtServer.h
|
||||
src/framework/RESTAPI_ExtServer.cpp
|
||||
src/framework/RESTAPI_IntServer.cpp
|
||||
src/framework/RESTAPI_IntServer.h
|
||||
src/framework/RESTAPI_SystemCommand.h
|
||||
src/framework/RESTAPI_WebSocketServer.h
|
||||
src/framework/EventBusManager.cpp
|
||||
src/framework/EventBusManager.h
|
||||
src/framework/RESTAPI_PartHandler.h
|
||||
src/framework/MicroService.cpp
|
||||
src/framework/MicroServiceExtra.h
|
||||
src/framework/ConfigurationValidator.cpp
|
||||
src/framework/ConfigurationValidator.h
|
||||
src/framework/ow_constants.h
|
||||
src/framework/WebSocketClientNotifications.h
|
||||
src/RESTObjects/RESTAPI_SecurityObjects.h src/RESTObjects/RESTAPI_SecurityObjects.cpp
|
||||
src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h
|
||||
src/RESTObjects/RESTAPI_GWobjects.h src/RESTObjects/RESTAPI_GWobjects.cpp
|
||||
src/RESTObjects/RESTAPI_FMSObjects.h src/RESTObjects/RESTAPI_FMSObjects.cpp
|
||||
src/RESTObjects/RESTAPI_CertObjects.cpp src/RESTObjects/RESTAPI_CertObjects.h
|
||||
src/RESTObjects/RESTAPI_OWLSobjects.cpp src/RESTObjects/RESTAPI_OWLSobjects.h
|
||||
src/RESTObjects/RESTAPI_ProvObjects.cpp src/RESTObjects/RESTAPI_ProvObjects.h
|
||||
src/RESTObjects/RESTAPI_AnalyticsObjects.cpp src/RESTObjects/RESTAPI_AnalyticsObjects.h
|
||||
src/RESTObjects/RESTAPI_SubObjects.cpp src/RESTObjects/RESTAPI_SubObjects.h
|
||||
src/RESTAPI/RESTAPI_routers.cpp
|
||||
src/Daemon.cpp src/Daemon.h
|
||||
src/Dashboard.h src/Dashboard.cpp
|
||||
|
||||
66
Dockerfile
66
Dockerfile
@@ -1,16 +1,22 @@
|
||||
FROM alpine:3.15 AS build-base
|
||||
ARG DEBIAN_VERSION=11.5-slim
|
||||
ARG POCO_VERSION=poco-tip-v2
|
||||
ARG CPPKAFKA_VERSION=tip-v1
|
||||
ARG JSON_VALIDATOR_VERSION=2.1.0
|
||||
|
||||
RUN apk add --update --no-cache \
|
||||
FROM debian:$DEBIAN_VERSION AS build-base
|
||||
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
make cmake g++ git \
|
||||
unixodbc-dev postgresql-dev mariadb-dev \
|
||||
librdkafka-dev boost-dev openssl-dev \
|
||||
zlib-dev nlohmann-json \
|
||||
curl-dev
|
||||
libpq-dev libmariadb-dev libmariadbclient-dev-compat \
|
||||
librdkafka-dev libboost-all-dev libssl-dev \
|
||||
zlib1g-dev nlohmann-json3-dev ca-certificates libcurl4-openssl-dev libfmt-dev
|
||||
|
||||
FROM build-base AS poco-build
|
||||
|
||||
ADD https://api.github.com/repos/stephb9959/poco/git/refs/heads/master version.json
|
||||
RUN git clone https://github.com/stephb9959/poco /poco
|
||||
ARG POCO_VERSION
|
||||
|
||||
ADD https://api.github.com/repos/AriliaWireless/poco/git/refs/tags/${POCO_VERSION} version.json
|
||||
RUN git clone https://github.com/AriliaWireless/poco --branch ${POCO_VERSION} /poco
|
||||
|
||||
WORKDIR /poco
|
||||
RUN mkdir cmake-build
|
||||
@@ -21,8 +27,10 @@ RUN cmake --build . --target install
|
||||
|
||||
FROM build-base AS cppkafka-build
|
||||
|
||||
ADD https://api.github.com/repos/stephb9959/cppkafka/git/refs/heads/master version.json
|
||||
RUN git clone https://github.com/stephb9959/cppkafka /cppkafka
|
||||
ARG CPPKAFKA_VERSION
|
||||
|
||||
ADD https://api.github.com/repos/AriliaWireless/cppkafka/git/refs/tags/${CPPKAFKA_VERSION} version.json
|
||||
RUN git clone https://github.com/AriliaWireless/cppkafka --branch ${CPPKAFKA_VERSION} /cppkafka
|
||||
|
||||
WORKDIR /cppkafka
|
||||
RUN mkdir cmake-build
|
||||
@@ -33,8 +41,10 @@ RUN cmake --build . --target install
|
||||
|
||||
FROM build-base AS json-schema-validator-build
|
||||
|
||||
ADD https://api.github.com/repos/pboettch/json-schema-validator/git/refs/heads/master version.json
|
||||
RUN git clone https://github.com/pboettch/json-schema-validator /json-schema-validator
|
||||
ARG JSON_VALIDATOR_VERSION
|
||||
|
||||
ADD https://api.github.com/repos/pboettch/json-schema-validator/git/refs/tags/${JSON_VALIDATOR_VERSION} version.json
|
||||
RUN git clone https://github.com/pboettch/json-schema-validator --branch ${JSON_VALIDATOR_VERSION} /json-schema-validator
|
||||
|
||||
WORKDIR /json-schema-validator
|
||||
RUN mkdir cmake-build
|
||||
@@ -43,18 +53,6 @@ RUN cmake ..
|
||||
RUN make
|
||||
RUN make install
|
||||
|
||||
FROM build-base AS fmtlib-build
|
||||
|
||||
ADD https://api.github.com/repos/fmtlib/fmt/git/refs/heads/master version.json
|
||||
RUN git clone https://github.com/fmtlib/fmt /fmtlib
|
||||
|
||||
WORKDIR /fmtlib
|
||||
RUN mkdir cmake-build
|
||||
WORKDIR cmake-build
|
||||
RUN cmake ..
|
||||
RUN make
|
||||
RUN make install
|
||||
|
||||
FROM build-base AS owanalytics-build
|
||||
|
||||
ADD CMakeLists.txt build /owanalytics/
|
||||
@@ -68,8 +66,6 @@ COPY --from=cppkafka-build /usr/local/include /usr/local/include
|
||||
COPY --from=cppkafka-build /usr/local/lib /usr/local/lib
|
||||
COPY --from=json-schema-validator-build /usr/local/include /usr/local/include
|
||||
COPY --from=json-schema-validator-build /usr/local/lib /usr/local/lib
|
||||
COPY --from=fmtlib-build /usr/local/include /usr/local/include
|
||||
COPY --from=fmtlib-build /usr/local/lib /usr/local/lib
|
||||
|
||||
WORKDIR /owanalytics
|
||||
RUN mkdir cmake-build
|
||||
@@ -77,21 +73,21 @@ WORKDIR /owanalytics/cmake-build
|
||||
RUN cmake ..
|
||||
RUN cmake --build . --config Release -j8
|
||||
|
||||
FROM alpine:3.15
|
||||
FROM debian:$DEBIAN_VERSION
|
||||
|
||||
ENV OWANALYTICS_USER=owanalytics \
|
||||
OWANALYTICS_ROOT=/owanalytics-data \
|
||||
OWANALYTICS_CONFIG=/owanalytics-data
|
||||
|
||||
RUN addgroup -S "$OWANALYTICS_USER" && \
|
||||
adduser -S -G "$OWANALYTICS_USER" "$OWANALYTICS_USER"
|
||||
RUN useradd "$OWANALYTICS_USER"
|
||||
|
||||
RUN mkdir /openwifi
|
||||
RUN mkdir -p "$OWANALYTICS_ROOT" "$OWANALYTICS_CONFIG" && \
|
||||
chown "$OWANALYTICS_USER": "$OWANALYTICS_ROOT" "$OWANALYTICS_CONFIG"
|
||||
|
||||
RUN apk add --update --no-cache librdkafka su-exec gettext ca-certificates bash jq curl \
|
||||
mariadb-connector-c libpq unixodbc postgresql-client
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y \
|
||||
librdkafka++1 gosu gettext ca-certificates bash jq curl wget \
|
||||
libmariadb-dev-compat libpq5 postgresql-client libfmt7
|
||||
|
||||
COPY readiness_check /readiness_check
|
||||
COPY test_scripts/curl/cli /cli
|
||||
@@ -100,11 +96,13 @@ COPY owanalytics.properties.tmpl /
|
||||
COPY docker-entrypoint.sh /
|
||||
COPY wait-for-postgres.sh /
|
||||
RUN wget https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-ucentral-deploy/main/docker-compose/certs/restapi-ca.pem \
|
||||
-O /usr/local/share/ca-certificates/restapi-ca-selfsigned.pem
|
||||
-O /usr/local/share/ca-certificates/restapi-ca-selfsigned.crt
|
||||
|
||||
COPY --from=owanalytics-build /owanalytics/cmake-build/owanalytics /openwifi/owanalytics
|
||||
COPY --from=cppkafka-build /cppkafka/cmake-build/src/lib/* /usr/local/lib
|
||||
COPY --from=poco-build /poco/cmake-build/lib/* /usr/local/lib
|
||||
COPY --from=cppkafka-build /cppkafka/cmake-build/src/lib/* /usr/local/lib/
|
||||
COPY --from=poco-build /poco/cmake-build/lib/* /usr/local/lib/
|
||||
|
||||
RUN ldconfig
|
||||
|
||||
EXPOSE 16009 17009 16109
|
||||
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
# wlan-cloud-analytics
|
||||
OpenWiFi Analytics V0.01
|
||||
|
||||
## OpenAPI
|
||||
You may get static page with OpenAPI docs generated from the definition on [GitHub Page](https://telecominfraproject.github.io/wlan-cloud-analytics/).
|
||||
|
||||
Also you may use [Swagger UI](https://petstore.swagger.io/#/) with OpenAPI definition file raw link (i.e. [latest version file](https://raw.githubusercontent.com/Telecominfraproject/wlan-cloud-analytics/main/openapi/owanalytics.yaml)) to get interactive docs page.
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
if [ "$SELFSIGNED_CERTS" = 'true' ]; then
|
||||
update-ca-certificates
|
||||
fi
|
||||
|
||||
if [[ "$TEMPLATE_CONFIG" = 'true' && ! -f "$OWANALYTICS_CONFIG"/owanalytics.properties ]]; then
|
||||
if [[ "$TEMPLATE_CONFIG" = 'true' ]]; then
|
||||
RESTAPI_HOST_ROOTCA=${RESTAPI_HOST_ROOTCA:-"\$OWANALYTICS_ROOT/certs/restapi-ca.pem"} \
|
||||
RESTAPI_HOST_PORT=${RESTAPI_HOST_PORT:-"16009"} \
|
||||
RESTAPI_HOST_CERT=${RESTAPI_HOST_CERT:-"\$OWANALYTICS_ROOT/certs/restapi-cert.pem"} \
|
||||
@@ -24,6 +24,7 @@ if [[ "$TEMPLATE_CONFIG" = 'true' && ! -f "$OWANALYTICS_CONFIG"/owanalytics.prop
|
||||
SYSTEM_URI_PRIVATE=${SYSTEM_URI_PRIVATE:-"https://localhost:17009"} \
|
||||
SYSTEM_URI_PUBLIC=${SYSTEM_URI_PUBLIC:-"https://localhost:16009"} \
|
||||
SYSTEM_URI_UI=${SYSTEM_URI_UI:-"http://localhost"} \
|
||||
SECURITY_RESTAPI_DISABLE=${SECURITY_RESTAPI_DISABLE:-"false"} \
|
||||
KAFKA_ENABLE=${KAFKA_ENABLE:-"true"} \
|
||||
KAFKA_BROKERLIST=${KAFKA_BROKERLIST:-"localhost:9092"} \
|
||||
KAFKA_SSL_CA_LOCATION=${KAFKA_SSL_CA_LOCATION:-""} \
|
||||
@@ -48,7 +49,7 @@ if [ "$1" = '/openwifi/owanalytics' -a "$(id -u)" = '0' ]; then
|
||||
if [ "$RUN_CHOWN" = 'true' ]; then
|
||||
chown -R "$OWANALYTICS_USER": "$OWANALYTICS_ROOT" "$OWANALYTICS_CONFIG"
|
||||
fi
|
||||
exec su-exec "$OWANALYTICS_USER" "$@"
|
||||
exec gosu "$OWANALYTICS_USER" "$@"
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
|
||||
2
helm/.gitignore
vendored
2
helm/.gitignore
vendored
@@ -1 +1,3 @@
|
||||
*.swp
|
||||
Chart.lock
|
||||
charts/
|
||||
|
||||
@@ -5,14 +5,14 @@ name: owanalytics
|
||||
version: 0.1.0
|
||||
dependencies:
|
||||
- name: postgresql
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
repository: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
|
||||
version: 10.9.2
|
||||
condition: postgresql.enabled
|
||||
- name: mysql
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
repository: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
|
||||
version: 8.8.3
|
||||
condition: mysql.enabled
|
||||
- name: mariadb
|
||||
repository: https://charts.bitnami.com/bitnami
|
||||
repository: https://tip.jfrog.io/artifactory/tip-wlan-cloud-ucentral-helm/
|
||||
version: 9.4.2
|
||||
condition: mariadb.enabled
|
||||
|
||||
@@ -70,8 +70,8 @@ The following table lists the configurable parameters of the chart and their def
|
||||
| persistence.size | string | Defines PV size | `'10Gi'` |
|
||||
| public_env_variables | hash | Defines list of environment variables to be passed to the Analytics | |
|
||||
| configProperties | hash | Configuration properties that should be passed to the application in `owanalytics.properties`. May be passed by key in set (i.e. `configProperties."rtty\.token"`) | |
|
||||
| certs | hash | Defines files (keys and certificates) that should be passed to the Analytics (PEM format is adviced to be used) (see `volumes.owanalytics` on where it is mounted) | |
|
||||
|
||||
| existingCertsSecret | string | Existing Kubernetes secret containing all required certificates and private keys for microservice operation. If set, certificates from `certs` key are ignored | `""` |
|
||||
| certs | hash | Defines files (keys and certificates) that should be passed to the Gateway (PEM format is adviced to be used) (see `volumes.owanalytics` on where it is mounted). If `existingCertsSecret` is set, certificates passed this way will not be used. | |
|
||||
|
||||
Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example,
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{{- $root := . -}}
|
||||
{{- $storageType := index .Values.configProperties "storage.type" -}}
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@@ -46,6 +47,39 @@ spec:
|
||||
- -timeout
|
||||
- 600s
|
||||
|
||||
{{- if eq $storageType "postgresql" }}
|
||||
- name: wait-postgres
|
||||
image: "{{ .Values.images.owanalytics.repository }}:{{ .Values.images.owanalytics.tag }}"
|
||||
imagePullPolicy: {{ .Values.images.owanalytics.pullPolicy }}
|
||||
command:
|
||||
- /wait-for-postgres.sh
|
||||
- {{ index .Values.configProperties "storage.type.postgresql.host" }}
|
||||
- echo
|
||||
- "PostgreSQL is ready"
|
||||
env:
|
||||
- name: KUBERNETES_DEPLOYED
|
||||
value: "{{ now }}"
|
||||
{{- range $key, $value := .Values.public_env_variables }}
|
||||
- name: {{ $key }}
|
||||
value: {{ $value | quote }}
|
||||
{{- end }}
|
||||
{{- range $key, $value := .Values.secret_env_variables }}
|
||||
- name: {{ $key }}
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: {{ include "owanalytics.fullname" $root }}-env
|
||||
key: {{ $key }}
|
||||
{{- end }}
|
||||
volumeMounts:
|
||||
{{- range .Values.volumes.owanalytics }}
|
||||
- name: {{ .name }}
|
||||
mountPath: {{ .mountPath }}
|
||||
{{- if .subPath }}
|
||||
subPath: {{ .subPath }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
containers:
|
||||
|
||||
- name: owanalytics
|
||||
@@ -97,8 +131,10 @@ spec:
|
||||
{{- toYaml . | nindent 12 }}
|
||||
{{- end }}
|
||||
|
||||
{{- with .Values.securityContext }}
|
||||
securityContext:
|
||||
fsGroup: 101
|
||||
{{- toYaml . | nindent 8 }}
|
||||
{{- end }}
|
||||
|
||||
imagePullSecrets:
|
||||
{{- range $image, $imageValue := .Values.images }}
|
||||
|
||||
@@ -9,7 +9,7 @@ fullnameOverride: ""
|
||||
images:
|
||||
owanalytics:
|
||||
repository: tip-tip-wlan-cloud-ucentral.jfrog.io/owanalytics
|
||||
tag: main
|
||||
tag: v2.8.0
|
||||
pullPolicy: Always
|
||||
# regcred:
|
||||
# registry: tip-tip-wlan-cloud-ucentral.jfrog.io
|
||||
@@ -71,7 +71,7 @@ volumes:
|
||||
mountPath: /owanalytics-data/certs
|
||||
volumeDefinition: |
|
||||
secret:
|
||||
secretName: {{ include "owanalytics.fullname" . }}-certs
|
||||
secretName: {{ if .Values.existingCertsSecret }}{{ .Values.existingCertsSecret }}{{ else }}{{ include "owanalytics.fullname" . }}-certs{{ end }}
|
||||
# Change this if you want to use another volume type
|
||||
- name: persist
|
||||
mountPath: /owanalytics-data/persist
|
||||
@@ -91,6 +91,9 @@ resources: {}
|
||||
# cpu: 100m
|
||||
# memory: 128Mi
|
||||
|
||||
securityContext:
|
||||
fsGroup: 1000
|
||||
|
||||
nodeSelector: {}
|
||||
|
||||
tolerations: []
|
||||
@@ -198,6 +201,9 @@ configProperties:
|
||||
storage.type.mysql.username: stephb
|
||||
storage.type.mysql.password: snoopy99
|
||||
|
||||
# NOTE: List of required certificates may be found in "certs" key. Alternative way to pass required certificates is to create external secret with all required certificates and set secret name in "existingCertsSecret" key. Details may be found in https://github.com/Telecominfraproject/wlan-cloud-ucentral-deploy/tree/main/chart#tldr
|
||||
existingCertsSecret: ""
|
||||
|
||||
certs:
|
||||
# restapi-ca.pem: ""
|
||||
# restapi-cert.pem: ""
|
||||
|
||||
@@ -27,71 +27,13 @@ components:
|
||||
|
||||
responses:
|
||||
NotFound:
|
||||
description: The specified resource was not found.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
ErrorCode:
|
||||
type: integer
|
||||
ErrorDetails:
|
||||
type: string
|
||||
ErrorDescription:
|
||||
type: string
|
||||
|
||||
$ref: 'https://github.com/Telecominfraproject/wlan-cloud-ucentralsec/blob/main/openpapi/owsec.yaml#/components/responses/NotFound'
|
||||
Unauthorized:
|
||||
description: The requested does not have sufficient rights to perform the operation.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
ErrorCode:
|
||||
type: integer
|
||||
enum:
|
||||
- 0 # Success
|
||||
- 1 # PASSWORD_CHANGE_REQUIRED,
|
||||
- 2 # INVALID_CREDENTIALS,
|
||||
- 3 # PASSWORD_ALREADY_USED,
|
||||
- 4 # USERNAME_PENDING_VERIFICATION,
|
||||
- 5 # PASSWORD_INVALID,
|
||||
- 6 # INTERNAL_ERROR,
|
||||
- 7 # ACCESS_DENIED,
|
||||
- 8 # INVALID_TOKEN
|
||||
- 9 # EXPIRED_TOKEN
|
||||
- 10 # RATE_LIMIT_EXCEEDED
|
||||
- 11 # BAD_MFA_TRANSACTION
|
||||
- 12 # MFA_FAILURE
|
||||
- 13 # SECURITY_SERVICE_UNREACHABLE
|
||||
ErrorDetails:
|
||||
type: string
|
||||
ErrorDescription:
|
||||
type: string
|
||||
|
||||
$ref: 'https://github.com/Telecominfraproject/wlan-cloud-ucentralsec/blob/main/openpapi/owsec.yaml#/components/responses/Unauthorized'
|
||||
Success:
|
||||
description: The requested operation was performed.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
Operation:
|
||||
type: string
|
||||
Details:
|
||||
type: string
|
||||
Code:
|
||||
type: integer
|
||||
|
||||
$ref: 'https://github.com/Telecominfraproject/wlan-cloud-ucentralsec/blob/main/openpapi/owsec.yaml#/components/responses/Success'
|
||||
BadRequest:
|
||||
description: The requested operation failed.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
ErrorCode:
|
||||
type: integer
|
||||
ErrorDetails:
|
||||
type: string
|
||||
ErrorDescription:
|
||||
type: integer
|
||||
$ref: 'https://github.com/Telecominfraproject/wlan-cloud-ucentralsec/blob/main/openpapi/owsec.yaml#/components/responses/BadRequest'
|
||||
|
||||
schemas:
|
||||
ObjectInfo:
|
||||
@@ -1134,42 +1076,6 @@ paths:
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
|
||||
/iptocountry:
|
||||
get:
|
||||
tags:
|
||||
- Utility
|
||||
summary: Get the country code for an IP address
|
||||
operationId: getIpToCountry
|
||||
parameters:
|
||||
- in: query
|
||||
name: iplist
|
||||
schema:
|
||||
type: string
|
||||
example:
|
||||
10.2.2.2,10.3.4.3
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
description: List of country codes.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
enabled:
|
||||
type: boolean
|
||||
countryCodes:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
400:
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
403:
|
||||
$ref: '#/components/responses/Unauthorized'
|
||||
404:
|
||||
$ref: '#/components/responses/NotFound'
|
||||
|
||||
/wifiClientHistory:
|
||||
get:
|
||||
tags:
|
||||
|
||||
@@ -34,6 +34,7 @@ openwifi.system.uri.private = https://localhost:17009
|
||||
openwifi.system.uri.public = https://ucentral.dpaas.arilia.com:16009
|
||||
openwifi.system.commandchannel = /tmp/app.owanalytics
|
||||
openwifi.system.uri.ui = owprov-ui.arilia.com
|
||||
openwifi.security.restapi.disable = false
|
||||
|
||||
#############################
|
||||
# Generic information for all micro services
|
||||
@@ -101,4 +102,4 @@ storage.type.mysql.connectiontimeout = 60
|
||||
########################################################################
|
||||
logging.type = file
|
||||
logging.path = $OWANALYTICS_ROOT/logs
|
||||
logging.level = debug
|
||||
logging.level = debug
|
||||
|
||||
@@ -39,6 +39,7 @@ openwifi.system.uri.private = ${SYSTEM_URI_PRIVATE}
|
||||
openwifi.system.uri.public = ${SYSTEM_URI_PUBLIC}
|
||||
openwifi.system.commandchannel = /tmp/app.owanalytics
|
||||
openwifi.system.uri.ui = ${SYSTEM_URI_UI}
|
||||
openwifi.security.restapi.disable = ${SECURITY_RESTAPI_DISABLE}
|
||||
|
||||
#############################
|
||||
# Generic information for all micro services
|
||||
|
||||
109
src/APStats.cpp
109
src/APStats.cpp
@@ -6,6 +6,8 @@
|
||||
#include "dict_ssid.h"
|
||||
#include "StorageService.h"
|
||||
#include "WifiClientCache.h"
|
||||
#include "fmt/format.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
@@ -88,12 +90,19 @@ namespace OpenWifi {
|
||||
return false;
|
||||
}
|
||||
|
||||
static int BandToInt(const std::string &band) {
|
||||
if(band=="2G") return 2;
|
||||
if(band=="5G") return 5;
|
||||
if(band=="6G") return 6;
|
||||
return 2;
|
||||
}
|
||||
|
||||
void AP::UpdateStats(const std::shared_ptr<nlohmann::json> &State) {
|
||||
DI_.states++;
|
||||
DI_.connected =true;
|
||||
|
||||
AnalyticsObjects::DeviceTimePoint DTP;
|
||||
Logger().information(fmt::format("{}: stats message.", DI_.serialNumber));
|
||||
poco_information(Logger(),fmt::format("{}: stats message.", DI_.serialNumber));
|
||||
|
||||
// find radios first to get associations.
|
||||
try {
|
||||
@@ -124,7 +133,13 @@ namespace OpenWifi {
|
||||
if (radio.contains("channel")) {
|
||||
AnalyticsObjects::RadioTimePoint RTP;
|
||||
GetJSON("channel", radio, RTP.channel, (uint64_t) 2);
|
||||
RTP.band = RTP.channel <= 16 ? 2 : 5;
|
||||
if(radio.contains("band") && radio["band"].is_array()) {
|
||||
auto BandArray = radio["band"];
|
||||
RTP.band = BandToInt(BandArray[0]);
|
||||
// std::cout << "BAND (radio): " << BandToInt(BandArray[0]) << std::endl;
|
||||
} else {
|
||||
RTP.band = RTP.channel <= 16 ? 2 : 5;
|
||||
}
|
||||
radio_map[radio_index++] = std::make_pair(RTP.band, RTP.channel);
|
||||
GetJSON("busy_ms", radio, RTP.busy_ms, (uint64_t) 0);
|
||||
GetJSON("receive_ms", radio, RTP.receive_ms, (uint64_t) 0);
|
||||
@@ -158,6 +173,7 @@ namespace OpenWifi {
|
||||
auto interfaces = (*State)["interfaces"];
|
||||
DI_.associations_2g = DI_.associations_5g = DI_.associations_6g = 0;
|
||||
for(const auto &interface:interfaces) {
|
||||
std::string InterfaceName = fmt::format("{}: {}", DI_.serialNumber, interface.contains("name") ? to_string(interface["name"]) : "unknown");
|
||||
if(interface.contains("counters")) {
|
||||
auto counters = interface["counters"];
|
||||
GetJSON("collisions", counters, DTP.ap_data.collisions, (uint64_t) 0);
|
||||
@@ -172,13 +188,57 @@ namespace OpenWifi {
|
||||
GetJSON("tx_packets", counters, DTP.ap_data.tx_packets, (uint64_t) 0);
|
||||
}
|
||||
|
||||
InterfaceClientEntryMap_t ICEM;
|
||||
if(interface.contains("clients") && interface["clients"].is_array()) {
|
||||
try {
|
||||
auto Clients = interface["clients"];
|
||||
for(const auto & client: Clients) {
|
||||
if(client.contains("mac") && client["mac"].is_string()) {
|
||||
InterfaceClientEntry E;
|
||||
if(client.contains("ipv4_addresses") && client["ipv4_addresses"].is_array()) {
|
||||
for(const auto &ip:client["ipv4_addresses"]) {
|
||||
E.ipv4_addresses.push_back(ip);
|
||||
}
|
||||
}
|
||||
if(client.contains("ipv6_addresses") && client["ipv6_addresses"].is_array()) {
|
||||
for(const auto &ip:client["ipv6_addresses"]) {
|
||||
E.ipv6_addresses.push_back(ip);
|
||||
}
|
||||
}
|
||||
auto M = mac_filter(client["mac"]);
|
||||
ICEM[M] = E;
|
||||
}
|
||||
}
|
||||
} catch(...) {
|
||||
std::cout << "Exception while parsing clients: " << InterfaceName << std::endl;
|
||||
}
|
||||
} else {
|
||||
// std::cout <<"Interface: No clients: " << InterfaceName << std::endl;
|
||||
}
|
||||
|
||||
if(interface.contains("ssids")) {
|
||||
auto ssids = interface["ssids"];
|
||||
for (const auto &ssid: ssids) {
|
||||
AnalyticsObjects::SSIDTimePoint SSIDTP;
|
||||
uint radio_location=0;
|
||||
SSIDTP.band = 2;
|
||||
if(ssid.contains("radio")) {
|
||||
|
||||
if(ssid.contains("band")) {
|
||||
std::string Band = ssid["band"];
|
||||
SSIDTP.band = BandToInt(Band);
|
||||
// std::cout << "BAND (ssid): " << SSIDTP.band << std::endl;
|
||||
auto radio = ssid["radio"];
|
||||
if(radio.contains("$ref")) {
|
||||
auto ref = radio["$ref"];
|
||||
auto radio_parts = Poco::StringTokenizer(ref, "/");
|
||||
if(radio_parts.count()==3) {
|
||||
radio_location = std::strtol(radio_parts[2].c_str(), nullptr, 10);
|
||||
if(radio_map.find(radio_location)!=radio_map.end()) {
|
||||
SSIDTP.channel = radio_map[radio_location].second;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(ssid.contains("radio")) {
|
||||
auto radio = ssid["radio"];
|
||||
if(radio.contains("$ref")) {
|
||||
auto ref = radio["$ref"];
|
||||
@@ -243,8 +303,19 @@ namespace OpenWifi {
|
||||
GetJSON("rx_packets",association,WFH.rx_packets,(uint64_t)0);
|
||||
GetJSON("tx_packets",association,WFH.tx_packets,(uint64_t)0);
|
||||
|
||||
WFH.ipv4 = "---";
|
||||
WFH.ipv6 = "----";
|
||||
// try to locate the IP addresses
|
||||
auto ClientInfo = ICEM.find(WFH.station_id);
|
||||
if(ClientInfo!=end(ICEM)) {
|
||||
if(!ClientInfo->second.ipv4_addresses.empty()) {
|
||||
WFH.ipv4 = ClientInfo->second.ipv4_addresses[0];
|
||||
}
|
||||
if(!ClientInfo->second.ipv6_addresses.empty()) {
|
||||
WFH.ipv6 = ClientInfo->second.ipv6_addresses[0];
|
||||
}
|
||||
// std::cout << __LINE__ << ": " << InterfaceName << " Mac Found: " << ICEM.size() << " entries. " << WFH.station_id << std::endl;
|
||||
} else {
|
||||
// std::cout << __LINE__ << ": " << InterfaceName << " Mac NOT found: " << ICEM.size() << " entries. " << WFH.station_id << std::endl;
|
||||
}
|
||||
|
||||
for(const auto &rd:DTP.radio_data) {
|
||||
if(rd.band == SSIDTP.band) {
|
||||
@@ -311,7 +382,7 @@ namespace OpenWifi {
|
||||
}
|
||||
DTP.device_info = DI_;
|
||||
} catch (...) {
|
||||
Logger().information(fmt::format("{}: stats failed parsing.", DI_.serialNumber));
|
||||
poco_information(Logger(),fmt::format("{}: stats failed parsing.", DI_.serialNumber));
|
||||
}
|
||||
|
||||
if(got_base) {
|
||||
@@ -421,7 +492,7 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
if (got_connection && got_health) {
|
||||
db_DTP.id = MicroService::instance().CreateUUID();
|
||||
db_DTP.id = MicroServiceCreateUUID();
|
||||
db_DTP.boardId = boardId_;
|
||||
db_DTP.serialNumber = db_DTP.device_info.serialNumber;
|
||||
StorageService()->TimePointsDB().CreateRecord(db_DTP);
|
||||
@@ -435,49 +506,49 @@ namespace OpenWifi {
|
||||
|
||||
void AP::UpdateConnection(const std::shared_ptr<nlohmann::json> &Connection) {
|
||||
DI_.pings++;
|
||||
DI_.lastContact = OpenWifi::Now();
|
||||
DI_.lastContact = Utils::Now();
|
||||
try {
|
||||
if (Connection->contains("ping")) {
|
||||
got_connection = true;
|
||||
Logger().information(fmt::format("{}: ping message.", DI_.serialNumber));
|
||||
poco_debug(Logger(),fmt::format("{}: ping message.", DI_.serialNumber));
|
||||
DI_.connected = true;
|
||||
DI_.lastPing = OpenWifi::Now();
|
||||
DI_.lastPing = Utils::Now();
|
||||
auto ping = (*Connection)["ping"];
|
||||
GetJSON("compatible", ping, DI_.deviceType, std::string{} );
|
||||
GetJSON("connectionIp", ping, DI_.connectionIp, std::string{} );
|
||||
GetJSON("locale", ping, DI_.locale, std::string{} );
|
||||
GetJSON("timestamp", ping, DI_.lastConnection, (uint64_t) OpenWifi::Now() );
|
||||
GetJSON("timestamp", ping, DI_.lastConnection, (uint64_t) Utils::Now() );
|
||||
if (ping.contains("firmware")) {
|
||||
auto NewFirmware = ping["firmware"];
|
||||
if (NewFirmware != DI_.lastFirmware) {
|
||||
DI_.lastFirmware = NewFirmware;
|
||||
DI_.lastFirmwareUpdate = OpenWifi::Now();
|
||||
DI_.lastFirmwareUpdate = Utils::Now();
|
||||
}
|
||||
}
|
||||
} else if (Connection->contains("disconnection")) {
|
||||
Logger().information(fmt::format("{}: disconnection message.", DI_.serialNumber));
|
||||
poco_debug(Logger(),fmt::format("{}: disconnection message.", DI_.serialNumber));
|
||||
auto Disconnection = (*Connection)["disconnection"];
|
||||
GetJSON("timestamp", Disconnection, DI_.lastDisconnection, (uint64_t)0 );
|
||||
got_base = got_health = got_connection = false;
|
||||
DI_.connected = false;
|
||||
} else if (Connection->contains("capabilities")) {
|
||||
Logger().information(fmt::format("{}: connection message.", DI_.serialNumber));
|
||||
poco_debug(Logger(),fmt::format("{}: connection message.", DI_.serialNumber));
|
||||
got_connection = true;
|
||||
DI_.connected = true;
|
||||
DI_.lastConnection = OpenWifi::Now();
|
||||
DI_.lastConnection = Utils::Now();
|
||||
auto ConnectionData = (*Connection)["capabilities"];
|
||||
if (ConnectionData.contains("firmware")) {
|
||||
auto NewFirmware = ConnectionData["firmware"];
|
||||
if (NewFirmware != DI_.lastFirmware) {
|
||||
DI_.lastFirmware = NewFirmware;
|
||||
DI_.lastFirmwareUpdate = OpenWifi::Now();
|
||||
DI_.lastFirmwareUpdate = Utils::Now();
|
||||
}
|
||||
}
|
||||
GetJSON("connectionIp", ConnectionData, DI_.connectionIp, std::string{} );
|
||||
GetJSON("locale", ConnectionData, DI_.locale, std::string{} );
|
||||
}
|
||||
} catch (...) {
|
||||
Logger().information(fmt::format("{}: error parsing connection message.", DI_.serialNumber));
|
||||
poco_information(Logger(),fmt::format("{}: error parsing connection message.", DI_.serialNumber));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,9 +557,9 @@ namespace OpenWifi {
|
||||
got_health = true;
|
||||
GetJSON("timestamp", *Health, DI_.lastHealth, (uint64_t)0 );
|
||||
GetJSON("sanity", *Health, DI_.health, (uint64_t)0 );
|
||||
Logger().information(fmt::format("{}: health message.", DI_.serialNumber));
|
||||
poco_debug(Logger(),fmt::format("{}: health message.", DI_.serialNumber));
|
||||
} catch(...) {
|
||||
Logger().information(fmt::format("{}: error parsing health message.", DI_.serialNumber));
|
||||
poco_information(Logger(),fmt::format("{}: error parsing health message.", DI_.serialNumber));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include "framework/MicroService.h"
|
||||
#include "nlohmann/json.hpp"
|
||||
#include "RESTObjects/RESTAPI_AnalyticsObjects.h"
|
||||
#include "framework/utils.h"
|
||||
#include "Poco/Logger.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
struct InterfaceClientEntry {
|
||||
std::vector<std::string> ipv4_addresses;
|
||||
std::vector<std::string> ipv6_addresses;
|
||||
};
|
||||
|
||||
using InterfaceClientEntryMap_t = std::map<std::string,InterfaceClientEntry>;
|
||||
|
||||
class AP {
|
||||
public:
|
||||
explicit AP(uint64_t mac, const std::string &venue_id, const std::string &BoardId, Poco::Logger &L) :
|
||||
|
||||
@@ -6,17 +6,21 @@
|
||||
// Arilia Wireless Inc.
|
||||
//
|
||||
|
||||
#include "Daemon.h"
|
||||
|
||||
#include "Poco/Util/Application.h"
|
||||
#include "Poco/Util/Option.h"
|
||||
#include "Poco/Environment.h"
|
||||
#include "Poco/Net/SSLManager.h"
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
|
||||
#include "Daemon.h"
|
||||
#include "StorageService.h"
|
||||
#include "VenueCoordinator.h"
|
||||
#include "StateReceiver.h"
|
||||
#include "DeviceStatusReceiver.h"
|
||||
#include "HealthReceiver.h"
|
||||
#include "WifiClientCache.h"
|
||||
#include "framework/UI_WebSocketClientServer.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class Daemon *Daemon::instance_ = nullptr;
|
||||
@@ -34,7 +38,8 @@ namespace OpenWifi {
|
||||
DeviceStatusReceiver(),
|
||||
HealthReceiver(),
|
||||
VenueCoordinator(),
|
||||
WifiClientCache()
|
||||
WifiClientCache(),
|
||||
UI_WebSocketClientServer()
|
||||
});
|
||||
}
|
||||
return instance_;
|
||||
@@ -42,20 +47,32 @@ namespace OpenWifi {
|
||||
|
||||
void Daemon::PostInitialization([[maybe_unused]] Poco::Util::Application &self) {
|
||||
}
|
||||
|
||||
void DaemonPostInitialization(Poco::Util::Application &self) {
|
||||
Daemon()->PostInitialization(self);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
try {
|
||||
auto App = OpenWifi::Daemon::instance();
|
||||
auto ExitCode = App->run(argc, argv);
|
||||
delete App;
|
||||
int ExitCode;
|
||||
try {
|
||||
Poco::Net::SSLManager::instance().initializeServer(nullptr, nullptr, nullptr);
|
||||
auto App = OpenWifi::Daemon::instance();
|
||||
ExitCode = App->run(argc, argv);
|
||||
Poco::Net::SSLManager::instance().shutdown();
|
||||
} catch (Poco::Exception &exc) {
|
||||
ExitCode = Poco::Util::Application::EXIT_SOFTWARE;
|
||||
std::cout << exc.displayText() << std::endl;
|
||||
} catch (std::exception &exc) {
|
||||
ExitCode = Poco::Util::Application::EXIT_TEMPFAIL;
|
||||
std::cout << exc.what() << std::endl;
|
||||
} catch (...) {
|
||||
ExitCode = Poco::Util::Application::EXIT_TEMPFAIL;
|
||||
std::cout << "Exception on closure" << std::endl;
|
||||
}
|
||||
|
||||
return ExitCode;
|
||||
|
||||
} catch (Poco::Exception &exc) {
|
||||
std::cerr << exc.displayText() << std::endl;
|
||||
return Poco::Util::Application::EXIT_SOFTWARE;
|
||||
}
|
||||
std::cout << "Exitcode: " << ExitCode << std::endl;
|
||||
return ExitCode;
|
||||
}
|
||||
|
||||
// end of namespace
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "Dashboard.h"
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/MicroServiceNames.h"
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
#include "RESTObjects/RESTAPI_AnalyticsObjects.h"
|
||||
|
||||
@@ -47,8 +48,6 @@ namespace OpenWifi {
|
||||
};
|
||||
|
||||
inline Daemon * Daemon() { return Daemon::instance(); }
|
||||
inline void DaemonPostInitialization(Poco::Util::Application &self) {
|
||||
Daemon()->PostInitialization(self);
|
||||
}
|
||||
void DaemonPostInitialization(Poco::Util::Application &self);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
//
|
||||
|
||||
#include "Dashboard.h"
|
||||
#include "StorageService.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
void AnalyticsDashboard::Create() {
|
||||
uint64_t Now = OpenWifi::Now();
|
||||
uint64_t Now = Utils::Now();
|
||||
if(LastRun_==0 || (Now-LastRun_)>120) {
|
||||
DB_.reset();
|
||||
// Todo: call dashboard creation code.
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "DeviceStatusReceiver.h"
|
||||
#include "VenueWatcher.h"
|
||||
#include "fmt/core.h"
|
||||
#include "framework/KafkaTopics.h"
|
||||
#include "framework/KafkaManager.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
int DeviceStatusReceiver::Start() {
|
||||
@@ -23,6 +25,7 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
void DeviceStatusReceiver::run() {
|
||||
Utils::SetThreadName("dev-status");
|
||||
Poco::AutoPtr<Poco::Notification> Note(Queue_.waitDequeueNotification());
|
||||
while(Note && Running_) {
|
||||
auto Msg = dynamic_cast<DeviceStatusMessage *>(Note.get());
|
||||
@@ -74,7 +77,7 @@ namespace OpenWifi {
|
||||
|
||||
void DeviceStatusReceiver::DeviceStatusReceived(const std::string &Key, const std::string &Payload) {
|
||||
std::lock_guard G(Mutex_);
|
||||
Logger().information(fmt::format("Device({}): Connection/Ping message.", Key));
|
||||
poco_debug(Logger(),fmt::format("Device({}): Connection/Ping message.", Key));
|
||||
Queue_.enqueueNotification( new DeviceStatusMessage(Key,Payload));
|
||||
}
|
||||
}
|
||||
@@ -4,8 +4,9 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
// #include "VenueWatcher.h"
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "Poco/NotificationQueue.h"
|
||||
#include "Poco/Notification.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class DeviceStatusMessage : public Poco::Notification {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "HealthReceiver.h"
|
||||
#include "VenueWatcher.h"
|
||||
#include "framework/KafkaManager.h"
|
||||
#include "framework/KafkaTopics.h"
|
||||
#include "fmt/core.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
@@ -23,6 +25,7 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
void HealthReceiver::run() {
|
||||
Utils::SetThreadName("dev-health");
|
||||
Poco::AutoPtr<Poco::Notification> Note(Queue_.waitDequeueNotification());
|
||||
while(Note && Running_) {
|
||||
auto Msg = dynamic_cast<HealthMessage *>(Note.get());
|
||||
@@ -74,7 +77,7 @@ namespace OpenWifi {
|
||||
|
||||
void HealthReceiver::HealthReceived(const std::string &Key, const std::string &Payload) {
|
||||
std::lock_guard G(Mutex_);
|
||||
Logger().information(fmt::format("Device({}): Health message.", Key));
|
||||
poco_debug(Logger(),fmt::format("Device({}): Health message.", Key));
|
||||
Queue_.enqueueNotification( new HealthMessage(Key,Payload));
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,11 @@
|
||||
// Created by stephane bourque on 2022-03-15.
|
||||
//
|
||||
|
||||
#ifndef OWANALYTICS_HEALTHRECEIVER_H
|
||||
#define OWANALYTICS_HEALTHRECEIVER_H
|
||||
|
||||
|
||||
class HealthReceiver {
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif //OWANALYTICS_HEALTHRECEIVER_H
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
// #include "VenueWatcher.h"
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "Poco/Notification.h"
|
||||
#include "Poco/NotificationQueue.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class HealthMessage : public Poco::Notification {
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "RESTObjects/RESTAPI_AnalyticsObjects.h"
|
||||
#include "StorageService.h"
|
||||
#include "framework/orm.h"
|
||||
|
||||
@@ -8,8 +8,13 @@
|
||||
|
||||
namespace OpenWifi {
|
||||
void RESTAPI_board_devices_handler::DoGet() {
|
||||
|
||||
auto id = GetBinding("id","");
|
||||
|
||||
if(id.empty()) {
|
||||
return BadRequest(RESTAPI::Errors::MissingUUID);
|
||||
}
|
||||
|
||||
AnalyticsObjects::BoardInfo BI;
|
||||
if(!StorageService()->BoardsDB().GetRecord("id",id,BI)) {
|
||||
return NotFound();
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class RESTAPI_board_devices_handler : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_board_devices_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal)
|
||||
RESTAPI_board_devices_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>{
|
||||
Poco::Net::HTTPRequest::HTTP_GET,
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace OpenWifi {
|
||||
NewBoard.to_json(Answer);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
return InternalError(RESTAPI::Errors::RecordNotCreated);
|
||||
}
|
||||
|
||||
void RESTAPI_board_handler::DoPut() {
|
||||
@@ -89,7 +90,7 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
if(StorageService()->BoardsDB().UpdateRecord("id",Existing.info.id,Existing)) {
|
||||
VenueCoordinator()->ModifyBoard(Existing.info.id);
|
||||
VenueCoordinator()->UpdateBoard(Existing.info.id);
|
||||
AnalyticsObjects::BoardInfo NewBoard;
|
||||
StorageService()->BoardsDB().GetRecord("id",Existing.info.id,NewBoard);
|
||||
Poco::JSON::Object Answer;
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class RESTAPI_board_handler : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_board_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal)
|
||||
RESTAPI_board_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>{
|
||||
Poco::Net::HTTPRequest::HTTP_GET,
|
||||
|
||||
@@ -4,15 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
#include "StorageService.h"
|
||||
#include "RESTObjects/RESTAPI_AnalyticsObjects.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class RESTAPI_board_list_handler : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_board_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal)
|
||||
RESTAPI_board_list_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>{
|
||||
Poco::Net::HTTPRequest::HTTP_GET,
|
||||
|
||||
@@ -5,54 +5,11 @@
|
||||
#include "RESTAPI_board_timepoint_handler.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace OpenWifi {
|
||||
static auto find_number_of_buckets(std::vector<AnalyticsObjects::DeviceTimePoint> &p) {
|
||||
uint32_t buckets=0,cur_buckets=0;
|
||||
std::string current_serialNumber;
|
||||
for(const auto &i:p) {
|
||||
if(current_serialNumber.empty()) {
|
||||
current_serialNumber = i.device_info.serialNumber;
|
||||
cur_buckets=0;
|
||||
}
|
||||
if(current_serialNumber==i.device_info.serialNumber) {
|
||||
cur_buckets++;
|
||||
} else {
|
||||
buckets = std::max(buckets,cur_buckets);
|
||||
current_serialNumber=i.device_info.serialNumber;
|
||||
cur_buckets=1;
|
||||
}
|
||||
}
|
||||
return std::max(buckets,cur_buckets);
|
||||
}
|
||||
|
||||
typedef std::vector< std::vector<AnalyticsObjects::DeviceTimePoint>> split_points;
|
||||
|
||||
static void split_in_buckets([[maybe_unused]] uint32_t buckets,std::vector<AnalyticsObjects::DeviceTimePoint> &p,split_points &sp) {
|
||||
std::string cur_sn;
|
||||
uint32_t cur_bucket=0;
|
||||
for(const auto &i:p) {
|
||||
if(cur_sn.empty()) {
|
||||
cur_bucket=1;
|
||||
cur_sn=i.device_info.serialNumber;
|
||||
}
|
||||
if(cur_sn==i.device_info.serialNumber) {
|
||||
if (cur_bucket>sp.size()) {
|
||||
std::vector<AnalyticsObjects::DeviceTimePoint> tmp_p;
|
||||
tmp_p.push_back(i);
|
||||
sp.push_back(tmp_p);
|
||||
cur_bucket++;
|
||||
} else {
|
||||
sp[cur_bucket-1].push_back(i);
|
||||
cur_bucket++;
|
||||
}
|
||||
} else {
|
||||
cur_bucket=1;
|
||||
sp[cur_bucket-1].push_back(i);
|
||||
cur_bucket++;
|
||||
cur_sn=i.device_info.serialNumber;
|
||||
}
|
||||
}
|
||||
}
|
||||
typedef std::vector<std::pair<std::uint64_t , std::uint64_t >> bucket_timespans;
|
||||
typedef std::vector< std::vector<AnalyticsObjects::DeviceTimePoint>> split_points;
|
||||
|
||||
template <typename X, typename M> void AverageAPData( X T, const std::vector<M> &Values, AnalyticsObjects::AveragePoint &P) {
|
||||
if(Values.empty())
|
||||
@@ -89,6 +46,81 @@ namespace OpenWifi {
|
||||
P.avg = 0.0;
|
||||
}
|
||||
|
||||
static void NewSort(const AnalyticsObjects::DeviceTimePointList &l,split_points &sp) {
|
||||
|
||||
struct {
|
||||
bool operator()(const AnalyticsObjects::DeviceTimePoint &lhs, const AnalyticsObjects::DeviceTimePoint &rhs) const {
|
||||
if (lhs.device_info.serialNumber < rhs.device_info.serialNumber) return true;
|
||||
if (lhs.device_info.serialNumber > rhs.device_info.serialNumber) return false;
|
||||
return lhs.timestamp < rhs.timestamp;
|
||||
}
|
||||
} sort_serial_ts;
|
||||
|
||||
// attempt at finding an interval
|
||||
AnalyticsObjects::DeviceTimePointList tmp{l};
|
||||
std::sort(tmp.points.begin(),tmp.points.end(),sort_serial_ts);
|
||||
|
||||
std::string cur_ser;
|
||||
std::uint64_t cur_int=0,start_val, last_val, first_val = 0;
|
||||
for(const auto &point:tmp.points) {
|
||||
if(cur_ser.empty()) {
|
||||
start_val = point.timestamp;
|
||||
cur_ser = point.serialNumber;
|
||||
first_val = point.timestamp;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(cur_ser==point.serialNumber) {
|
||||
auto this_int = point.timestamp - start_val;
|
||||
if(cur_int) {
|
||||
if(this_int<cur_int) {
|
||||
cur_int = this_int;
|
||||
}
|
||||
} else {
|
||||
cur_int = this_int;
|
||||
}
|
||||
start_val = point.timestamp;
|
||||
} else {
|
||||
cur_ser = point.serialNumber;
|
||||
start_val = point.timestamp;
|
||||
}
|
||||
last_val = point.timestamp;
|
||||
}
|
||||
|
||||
// std::cout << "Intervals: " << cur_int << std::endl;
|
||||
|
||||
std::vector<std::pair<std::uint64_t,std::uint64_t>> time_slots; // timeslot 0 has <t1,t2>
|
||||
std::vector<std::set<std::string>> serial_numbers; // serial number already in a timeslot.
|
||||
|
||||
|
||||
std::uint64_t cur_first=first_val,cur_end=0;
|
||||
sp.clear();
|
||||
while(cur_end<last_val) {
|
||||
std::pair<std::uint64_t,std::uint64_t> e;
|
||||
e.first = cur_first;
|
||||
e.second = e.first + cur_int-1;
|
||||
cur_first = e.second+1;
|
||||
cur_end = e.second;
|
||||
time_slots.emplace_back(e);
|
||||
std::set<std::string> q;
|
||||
serial_numbers.emplace_back(q);
|
||||
std::vector<AnalyticsObjects::DeviceTimePoint> qq;
|
||||
sp.emplace_back(qq);
|
||||
}
|
||||
|
||||
for(const auto &point:tmp.points) {
|
||||
std::uint64_t slot_index=0;
|
||||
for(const auto &slot:time_slots) {
|
||||
if(point.timestamp >= slot.first && point.timestamp <= slot.second) {
|
||||
serial_numbers[slot_index].insert(point.serialNumber);
|
||||
sp[slot_index].emplace_back(point);
|
||||
}
|
||||
slot_index++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void RESTAPI_board_timepoint_handler::DoGet() {
|
||||
auto id = GetBinding("id","");
|
||||
if(id.empty() || !Utils::ValidUUID(id)) {
|
||||
@@ -102,7 +134,12 @@ namespace OpenWifi {
|
||||
|
||||
auto fromDate = GetParameter("fromDate",0);
|
||||
auto endDate = GetParameter("endDate",0);
|
||||
auto maxRecords = GetParameter("maxRecords",1000);
|
||||
std::uint64_t maxRecords;
|
||||
if(Request->has("limit"))
|
||||
maxRecords = QB_.Limit;
|
||||
else
|
||||
maxRecords = GetParameter("maxRecords",1000);
|
||||
|
||||
auto statsOnly = GetBoolParameter("statsOnly");
|
||||
auto pointsOnly = GetBoolParameter("pointsOnly");
|
||||
auto pointsStatsOnly = GetBoolParameter("pointsStatsOnly");
|
||||
@@ -115,71 +152,39 @@ namespace OpenWifi {
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
|
||||
auto Points = std::make_unique<AnalyticsObjects::DeviceTimePointList>();
|
||||
StorageService()->TimePointsDB().SelectRecords(id,fromDate, endDate, maxRecords, Points->points);
|
||||
AnalyticsObjects::DeviceTimePointList Points;
|
||||
StorageService()->TimePointsDB().SelectRecords(id,fromDate, endDate, maxRecords, Points.points);
|
||||
std::cout << "1 MaxRecords=" << maxRecords << " retrieved=" << Points.points.size() << std::endl;
|
||||
|
||||
// sort by timestamp & serial number.
|
||||
struct {
|
||||
bool operator()(const AnalyticsObjects::DeviceTimePoint &lhs, const AnalyticsObjects::DeviceTimePoint &rhs) const {
|
||||
if(lhs.device_info.serialNumber < rhs.device_info.serialNumber) return true;
|
||||
if(lhs.device_info.serialNumber > rhs.device_info.serialNumber) return false;
|
||||
return lhs.timestamp < rhs.timestamp;
|
||||
}
|
||||
} DeviceTimePoint_sort;
|
||||
split_points sp;
|
||||
|
||||
struct {
|
||||
bool operator()(const AnalyticsObjects::SSIDTimePoint &lhs, const AnalyticsObjects::SSIDTimePoint &rhs) const {
|
||||
if(lhs.ssid < rhs.ssid) return true;
|
||||
if(lhs.ssid > rhs.ssid) return false;
|
||||
return lhs.bssid < rhs.bssid;
|
||||
}
|
||||
} SSID_sort;
|
||||
NewSort(Points,sp);
|
||||
std::cout << __LINE__ << std::endl;
|
||||
|
||||
struct {
|
||||
bool operator()(const AnalyticsObjects::UETimePoint &lhs, const AnalyticsObjects::UETimePoint &rhs) const {
|
||||
if(lhs.station < rhs.station) return true;
|
||||
return false;
|
||||
}
|
||||
} Association_sort;
|
||||
|
||||
std::sort( Points->points.begin(), Points->points.end(), DeviceTimePoint_sort);
|
||||
auto BucketsNeeded = find_number_of_buckets(Points->points);
|
||||
|
||||
auto sp = std::make_unique<split_points>();
|
||||
split_in_buckets(BucketsNeeded,Points->points, *sp);
|
||||
// must sort each bucket according to serial number.
|
||||
for(auto &i: *sp) {
|
||||
std::sort(i.begin(),i.end(),DeviceTimePoint_sort);
|
||||
// now sort according to UEs within a block
|
||||
for(auto &j:i) {
|
||||
std::sort(j.ssid_data.begin(),j.ssid_data.end(),SSID_sort);
|
||||
for(auto &k:j.ssid_data) {
|
||||
std::sort(k.associations.begin(),k.associations.end(),Association_sort);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Answer = std::make_unique<Poco::JSON::Object>();
|
||||
Poco::JSON::Object Answer;
|
||||
if(!pointsStatsOnly) {
|
||||
auto Points_OuterArray = std::make_unique<Poco::JSON::Array>();
|
||||
for (const auto &point_list:*sp) {
|
||||
Poco::JSON::Array Points_OuterArray;
|
||||
for (const auto &point_list:sp) {
|
||||
Poco::JSON::Array Points_InnerArray;
|
||||
for (const auto &point: point_list) {
|
||||
Poco::JSON::Object O;
|
||||
point.to_json(O);
|
||||
Points_InnerArray.add(O);
|
||||
}
|
||||
Points_OuterArray->add(Points_InnerArray);
|
||||
Points_OuterArray.add(Points_InnerArray);
|
||||
}
|
||||
Answer->set("points",*Points_OuterArray);
|
||||
Answer.set("points",Points_OuterArray);
|
||||
}
|
||||
|
||||
// calculate the stats for each time slot
|
||||
if(!pointsOnly) {
|
||||
auto Stats_Array = std::make_unique<Poco::JSON::Array>();
|
||||
for (const auto &point_list:*sp) {
|
||||
Poco::JSON::Array Stats_Array;
|
||||
for (const auto &point_list:sp) {
|
||||
AnalyticsObjects::DeviceTimePointAnalysis DTPA;
|
||||
|
||||
if(point_list.empty())
|
||||
continue;
|
||||
|
||||
DTPA.timestamp = point_list[0].timestamp;
|
||||
AverageAPData(&AnalyticsObjects::APTimePoint::tx_bytes_bw, point_list, DTPA.tx_bytes_bw);
|
||||
AverageAPData(&AnalyticsObjects::APTimePoint::rx_bytes_bw, point_list, DTPA.rx_bytes_bw);
|
||||
@@ -200,12 +205,12 @@ namespace OpenWifi {
|
||||
|
||||
Poco::JSON::Object Stats_point;
|
||||
DTPA.to_json(Stats_point);
|
||||
Stats_Array->add(Stats_point);
|
||||
Stats_Array.add(Stats_point);
|
||||
}
|
||||
Answer->set("stats", *Stats_Array);
|
||||
Answer.set("stats", Stats_Array);
|
||||
}
|
||||
|
||||
return ReturnObject(*Answer);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
|
||||
void RESTAPI_board_timepoint_handler::DoDelete() {
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class RESTAPI_board_timepoint_handler : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_board_timepoint_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal)
|
||||
RESTAPI_board_timepoint_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>{
|
||||
Poco::Net::HTTPRequest::HTTP_GET,
|
||||
|
||||
@@ -2,17 +2,18 @@
|
||||
// Created by stephane bourque on 2021-10-23.
|
||||
//
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "RESTAPI/RESTAPI_board_list_handler.h"
|
||||
#include "RESTAPI/RESTAPI_board_handler.h"
|
||||
#include "RESTAPI/RESTAPI_board_devices_handler.h"
|
||||
#include "RESTAPI/RESTAPI_board_timepoint_handler.h"
|
||||
#include "RESTAPI/RESTAPI_wificlienthistory_handler.h"
|
||||
#include "framework/RESTAPI_SystemCommand.h"
|
||||
#include "framework/RESTAPI_WebSocketServer.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
Poco::Net::HTTPRequestHandler * RESTAPI_ExtRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings,
|
||||
Poco::Logger & L, RESTAPI_GenericServer & S,
|
||||
Poco::Logger & L, RESTAPI_GenericServerAccounting & S,
|
||||
uint64_t TransactionId) {
|
||||
return RESTAPI_Router<
|
||||
RESTAPI_system_command,
|
||||
@@ -20,12 +21,13 @@ namespace OpenWifi {
|
||||
RESTAPI_board_timepoint_handler,
|
||||
RESTAPI_board_handler,
|
||||
RESTAPI_board_list_handler,
|
||||
RESTAPI_wificlienthistory_handler
|
||||
RESTAPI_wificlienthistory_handler,
|
||||
RESTAPI_webSocketServer
|
||||
>(Path,Bindings,L, S, TransactionId);
|
||||
}
|
||||
|
||||
Poco::Net::HTTPRequestHandler * RESTAPI_IntRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings,
|
||||
Poco::Logger & L, RESTAPI_GenericServer & S,
|
||||
Poco::Logger & L, RESTAPI_GenericServerAccounting & S,
|
||||
uint64_t TransactionId) {
|
||||
return RESTAPI_Router_I<
|
||||
RESTAPI_system_command,
|
||||
|
||||
@@ -20,10 +20,9 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
if(GetBoolParameter("macsOnly")) {
|
||||
|
||||
auto macFilter = GetParameter("macFilter","");
|
||||
std::vector<uint64_t> Macs;
|
||||
WifiClientCache()->FindNumbers(venue,macFilter,500,Macs);
|
||||
WifiClientCache()->FindNumbers(venue,macFilter,QB_.Offset, QB_.Limit, Macs);
|
||||
Poco::JSON::Array Arr;
|
||||
for(const auto &mac: Macs)
|
||||
Arr.add(Utils::IntToSerialNumber(mac));
|
||||
@@ -63,11 +62,8 @@ namespace OpenWifi {
|
||||
return ReturnCountOnly(Count);
|
||||
}
|
||||
|
||||
if(StorageService()->WifiClientHistoryDB().GetRecords(QB_.Offset,QB_.Limit, Results, Where, OrderBy)) {
|
||||
return ReturnObject("entries",Results);
|
||||
}
|
||||
|
||||
return BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
StorageService()->WifiClientHistoryDB().GetRecords(QB_.Offset,QB_.Limit, Results, Where, OrderBy);
|
||||
return ReturnObject("entries",Results);
|
||||
}
|
||||
|
||||
void RESTAPI_wificlienthistory_handler::DoDelete() {
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
#include "StorageService.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class RESTAPI_wificlienthistory_handler : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_wificlienthistory_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServer & Server, uint64_t TransactionId, bool Internal)
|
||||
RESTAPI_wificlienthistory_handler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>{
|
||||
Poco::Net::HTTPRequest::HTTP_GET,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#include "RESTAPI_AnalyticsObjects.h"
|
||||
#include "RESTAPI_ProvObjects.h"
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/RESTAPI_utils.h"
|
||||
|
||||
using OpenWifi::RESTAPI_utils::field_to_json;
|
||||
using OpenWifi::RESTAPI_utils::field_from_json;
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "RESTAPI_ProvObjects.h"
|
||||
#include "framework/utils.h"
|
||||
#include <vector>
|
||||
|
||||
namespace OpenWifi {
|
||||
@@ -375,7 +376,7 @@ namespace OpenWifi {
|
||||
};
|
||||
|
||||
struct WifiClientHistory {
|
||||
uint64_t timestamp=OpenWifi::Now();
|
||||
uint64_t timestamp=Utils::Now();
|
||||
std::string station_id;
|
||||
std::string bssid;
|
||||
std::string ssid;
|
||||
|
||||
@@ -3,176 +3,208 @@
|
||||
//
|
||||
|
||||
#include "RESTAPI_CertObjects.h"
|
||||
#include "framework/RESTAPI_utils.h"
|
||||
|
||||
using OpenWifi::RESTAPI_utils::field_to_json;
|
||||
using OpenWifi::RESTAPI_utils::field_from_json;
|
||||
|
||||
namespace OpenWifi {
|
||||
namespace CertObjects {
|
||||
void CertificateEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"type", type);
|
||||
field_to_json(Obj,"status", status);
|
||||
field_to_json(Obj,"certificate", certificate);
|
||||
field_to_json(Obj,"key", key);
|
||||
field_to_json(Obj,"devid", devid);
|
||||
field_to_json(Obj,"cas", cas);
|
||||
field_to_json(Obj,"manufacturer", manufacturer);
|
||||
field_to_json(Obj,"model", model);
|
||||
field_to_json(Obj,"redirector", redirector);
|
||||
field_to_json(Obj,"commonName", commonName);
|
||||
field_to_json(Obj,"certificateId", certificateId);
|
||||
field_to_json(Obj,"batch", batch);
|
||||
field_to_json(Obj,"created", created);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
field_to_json(Obj,"revoked", revoked);
|
||||
field_to_json(Obj,"revokeCount", revokeCount);
|
||||
}
|
||||
namespace OpenWifi::CertObjects {
|
||||
void CertificateEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"type", type);
|
||||
field_to_json(Obj,"status", status);
|
||||
field_to_json(Obj,"certificate", certificate);
|
||||
field_to_json(Obj,"key", key);
|
||||
field_to_json(Obj,"devid", devid);
|
||||
field_to_json(Obj,"cas", cas);
|
||||
field_to_json(Obj,"manufacturer", manufacturer);
|
||||
field_to_json(Obj,"model", model);
|
||||
field_to_json(Obj,"redirector", redirector);
|
||||
field_to_json(Obj,"commonName", commonName);
|
||||
field_to_json(Obj,"certificateId", certificateId);
|
||||
field_to_json(Obj,"batch", batch);
|
||||
field_to_json(Obj,"created", created);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
field_to_json(Obj,"revoked", revoked);
|
||||
field_to_json(Obj,"revokeCount", revokeCount);
|
||||
field_to_json(Obj,"synched", synched);
|
||||
}
|
||||
|
||||
bool CertificateEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"type", type);
|
||||
field_from_json(Obj,"status", status);
|
||||
field_from_json(Obj,"certificate", certificate);
|
||||
field_from_json(Obj,"key", key);
|
||||
field_from_json(Obj,"devid", devid);
|
||||
field_from_json(Obj,"cas", cas);
|
||||
field_from_json(Obj,"manufacturer", manufacturer);
|
||||
field_from_json(Obj,"model", model);
|
||||
field_from_json(Obj,"redirector", redirector);
|
||||
field_from_json(Obj,"commonName", commonName);
|
||||
field_from_json(Obj,"certificateId", certificateId);
|
||||
field_from_json(Obj,"batch", batch);
|
||||
field_from_json(Obj,"created", created);
|
||||
field_from_json(Obj,"modified", modified);
|
||||
field_from_json(Obj,"revoked", revoked);
|
||||
field_from_json(Obj,"revokeCount", revokeCount);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
bool CertificateEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"type", type);
|
||||
field_from_json(Obj,"status", status);
|
||||
field_from_json(Obj,"certificate", certificate);
|
||||
field_from_json(Obj,"key", key);
|
||||
field_from_json(Obj,"devid", devid);
|
||||
field_from_json(Obj,"cas", cas);
|
||||
field_from_json(Obj,"manufacturer", manufacturer);
|
||||
field_from_json(Obj,"model", model);
|
||||
field_from_json(Obj,"redirector", redirector);
|
||||
field_from_json(Obj,"commonName", commonName);
|
||||
field_from_json(Obj,"certificateId", certificateId);
|
||||
field_from_json(Obj,"batch", batch);
|
||||
field_from_json(Obj,"created", created);
|
||||
field_from_json(Obj,"modified", modified);
|
||||
field_from_json(Obj,"revoked", revoked);
|
||||
field_from_json(Obj,"revokeCount", revokeCount);
|
||||
field_from_json(Obj,"synched", synched);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EntityEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"name", name);
|
||||
field_to_json(Obj,"description", description);
|
||||
field_to_json(Obj,"defaultRedirector", defaultRedirector);
|
||||
field_to_json(Obj,"apiKey", apiKey);
|
||||
field_to_json(Obj,"serverEnrollmentProfile", serverEnrollmentProfile);
|
||||
field_to_json(Obj,"clientEnrollmentProfile", clientEnrollmentProfile);
|
||||
field_to_json(Obj,"organization", organization);
|
||||
field_to_json(Obj,"created", created);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
field_to_json(Obj,"suspended", suspended);
|
||||
field_to_json(Obj,"deleted", deleted);
|
||||
field_to_json(Obj,"notes", notes);
|
||||
}
|
||||
void EntityEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"name", name);
|
||||
field_to_json(Obj,"description", description);
|
||||
field_to_json(Obj,"defaultRedirector", defaultRedirector);
|
||||
field_to_json(Obj,"apiKey", apiKey);
|
||||
field_to_json(Obj,"serverEnrollmentProfile", serverEnrollmentProfile);
|
||||
field_to_json(Obj,"clientEnrollmentProfile", clientEnrollmentProfile);
|
||||
field_to_json(Obj,"organization", organization);
|
||||
field_to_json(Obj,"created", created);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
field_to_json(Obj,"suspended", suspended);
|
||||
field_to_json(Obj,"deleted", deleted);
|
||||
field_to_json(Obj,"notes", notes);
|
||||
}
|
||||
|
||||
bool EntityEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"name", name);
|
||||
field_from_json(Obj,"description", description);
|
||||
field_from_json(Obj,"defaultRedirector", defaultRedirector);
|
||||
field_from_json(Obj,"apiKey", apiKey);
|
||||
field_from_json(Obj,"serverEnrollmentProfile", serverEnrollmentProfile);
|
||||
field_from_json(Obj,"clientEnrollmentProfile", clientEnrollmentProfile);
|
||||
field_from_json(Obj,"organization", organization);
|
||||
field_from_json(Obj,"created", created);
|
||||
field_from_json(Obj,"modified", modified);
|
||||
field_from_json(Obj,"suspended", suspended);
|
||||
field_from_json(Obj,"deleted", deleted);
|
||||
field_from_json(Obj,"notes", notes);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
bool EntityEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"name", name);
|
||||
field_from_json(Obj,"description", description);
|
||||
field_from_json(Obj,"defaultRedirector", defaultRedirector);
|
||||
field_from_json(Obj,"apiKey", apiKey);
|
||||
field_from_json(Obj,"serverEnrollmentProfile", serverEnrollmentProfile);
|
||||
field_from_json(Obj,"clientEnrollmentProfile", clientEnrollmentProfile);
|
||||
field_from_json(Obj,"organization", organization);
|
||||
field_from_json(Obj,"created", created);
|
||||
field_from_json(Obj,"modified", modified);
|
||||
field_from_json(Obj,"suspended", suspended);
|
||||
field_from_json(Obj,"deleted", deleted);
|
||||
field_from_json(Obj,"notes", notes);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BatchEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"name", name);
|
||||
field_to_json(Obj,"description", description);
|
||||
field_to_json(Obj,"manufacturer", manufacturer);
|
||||
field_to_json(Obj,"model", model);
|
||||
field_to_json(Obj,"redirector", redirector);
|
||||
field_to_json(Obj,"commonNames", commonNames);
|
||||
field_to_json(Obj,"jobHistory", jobHistory);
|
||||
field_to_json(Obj,"notes", notes);
|
||||
field_to_json(Obj,"submitted", submitted);
|
||||
field_to_json(Obj,"started", started);
|
||||
field_to_json(Obj,"completed", completed);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
}
|
||||
void BatchEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"name", name);
|
||||
field_to_json(Obj,"description", description);
|
||||
field_to_json(Obj,"manufacturer", manufacturer);
|
||||
field_to_json(Obj,"model", model);
|
||||
field_to_json(Obj,"redirector", redirector);
|
||||
field_to_json(Obj,"commonNames", commonNames);
|
||||
field_to_json(Obj,"jobHistory", jobHistory);
|
||||
field_to_json(Obj,"notes", notes);
|
||||
field_to_json(Obj,"submitted", submitted);
|
||||
field_to_json(Obj,"started", started);
|
||||
field_to_json(Obj,"completed", completed);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
}
|
||||
|
||||
bool BatchEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"name", name);
|
||||
field_from_json(Obj,"description", description);
|
||||
field_from_json(Obj,"manufacturer", manufacturer);
|
||||
field_from_json(Obj,"model", model);
|
||||
field_from_json(Obj,"redirector", redirector);
|
||||
field_from_json(Obj,"commonNames", commonNames);
|
||||
field_from_json(Obj,"jobHistory", jobHistory);
|
||||
field_from_json(Obj,"notes", notes);
|
||||
field_from_json(Obj,"submitted", submitted);
|
||||
field_from_json(Obj,"started", started);
|
||||
field_from_json(Obj,"completed", completed);
|
||||
field_from_json(Obj,"modified", modified);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
bool BatchEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"name", name);
|
||||
field_from_json(Obj,"description", description);
|
||||
field_from_json(Obj,"manufacturer", manufacturer);
|
||||
field_from_json(Obj,"model", model);
|
||||
field_from_json(Obj,"redirector", redirector);
|
||||
field_from_json(Obj,"commonNames", commonNames);
|
||||
field_from_json(Obj,"jobHistory", jobHistory);
|
||||
field_from_json(Obj,"notes", notes);
|
||||
field_from_json(Obj,"submitted", submitted);
|
||||
field_from_json(Obj,"started", started);
|
||||
field_from_json(Obj,"completed", completed);
|
||||
field_from_json(Obj,"modified", modified);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void JobEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"batch", batch);
|
||||
field_to_json(Obj,"commonNames", commonNames);
|
||||
field_to_json(Obj,"completedNames", completedNames);
|
||||
field_to_json(Obj,"errorNames", errorNames);
|
||||
field_to_json(Obj,"status", status);
|
||||
field_to_json(Obj,"command", command);
|
||||
field_to_json(Obj,"parameters", parameters);
|
||||
field_to_json(Obj,"submitted", submitted);
|
||||
field_to_json(Obj,"started", started);
|
||||
field_to_json(Obj,"completed", completed);
|
||||
}
|
||||
void JobEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"creator", creator);
|
||||
field_to_json(Obj,"batch", batch);
|
||||
field_to_json(Obj,"commonNames", commonNames);
|
||||
field_to_json(Obj,"completedNames", completedNames);
|
||||
field_to_json(Obj,"errorNames", errorNames);
|
||||
field_to_json(Obj,"status", status);
|
||||
field_to_json(Obj,"command", command);
|
||||
field_to_json(Obj,"parameters", parameters);
|
||||
field_to_json(Obj,"submitted", submitted);
|
||||
field_to_json(Obj,"started", started);
|
||||
field_to_json(Obj,"completed", completed);
|
||||
field_to_json(Obj,"requesterUsername", requesterUsername);
|
||||
}
|
||||
|
||||
bool JobEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"batch", batch);
|
||||
field_from_json(Obj,"commonNames", commonNames);
|
||||
field_from_json(Obj,"completedNames", completedNames);
|
||||
field_from_json(Obj,"errorNames", errorNames);
|
||||
field_from_json(Obj,"status", status);
|
||||
field_from_json(Obj,"command", command);
|
||||
field_from_json(Obj,"parameters", parameters);
|
||||
field_from_json(Obj,"submitted", submitted);
|
||||
field_from_json(Obj,"started", started);
|
||||
field_from_json(Obj,"completed", completed);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
bool JobEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"creator", creator);
|
||||
field_from_json(Obj,"batch", batch);
|
||||
field_from_json(Obj,"commonNames", commonNames);
|
||||
field_from_json(Obj,"completedNames", completedNames);
|
||||
field_from_json(Obj,"errorNames", errorNames);
|
||||
field_from_json(Obj,"status", status);
|
||||
field_from_json(Obj,"command", command);
|
||||
field_from_json(Obj,"parameters", parameters);
|
||||
field_from_json(Obj,"submitted", submitted);
|
||||
field_from_json(Obj,"started", started);
|
||||
field_from_json(Obj,"completed", completed);
|
||||
field_from_json(Obj,"requesterUsername", requesterUsername);
|
||||
return true;
|
||||
} catch (...) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DashBoardYearlyStats::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "year", year);
|
||||
field_to_json(Obj, "activeCerts", activeCerts);
|
||||
field_to_json(Obj, "revokedCerts", revokedCerts);
|
||||
}
|
||||
|
||||
void Dashboard::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"snapshot", snapshot);
|
||||
field_to_json(Obj,"numberOfIssuedCerts", numberOfIssuedCerts);
|
||||
field_to_json(Obj,"numberOfRevokedCerts", numberOfRevokedCerts);
|
||||
field_to_json(Obj,"activeCertsPerOrganization", activeCertsPerOrganization);
|
||||
field_to_json(Obj,"revokedCertsPerOrganization", revokedCertsPerOrganization);
|
||||
field_to_json(Obj,"numberOfRedirectors", numberOfRedirectors);
|
||||
field_to_json(Obj,"deviceTypes", deviceTypes);
|
||||
field_to_json(Obj,"monthlyNumberOfCerts", monthlyNumberOfCerts);
|
||||
field_to_json(Obj,"monthlyNumberOfCertsPerOrgPerYear", monthlyNumberOfCertsPerOrgPerYear);
|
||||
}
|
||||
|
||||
void Dashboard::reset() {
|
||||
snapshot=0;
|
||||
numberOfRevokedCerts = numberOfIssuedCerts = 0;
|
||||
activeCertsPerOrganization.clear();
|
||||
revokedCertsPerOrganization.clear();
|
||||
numberOfRedirectors.clear();
|
||||
deviceTypes.clear();
|
||||
monthlyNumberOfCerts.clear();
|
||||
monthlyNumberOfCertsPerOrgPerYear.clear();
|
||||
}
|
||||
}
|
||||
@@ -5,97 +5,119 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
namespace OpenWifi::CertObjects {
|
||||
|
||||
namespace CertObjects {
|
||||
struct CertificateEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t entity;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
std::string type;
|
||||
std::string status;
|
||||
std::string certificate;
|
||||
std::string key;
|
||||
std::string devid;
|
||||
std::string cas;
|
||||
std::string manufacturer;
|
||||
std::string model;
|
||||
std::string redirector;
|
||||
std::string commonName;
|
||||
std::string certificateId;
|
||||
OpenWifi::Types::UUID_t batch;
|
||||
uint64_t created = 0;
|
||||
uint64_t modified = 0;
|
||||
uint64_t revoked = 0;
|
||||
uint64_t revokeCount = 0;
|
||||
uint64_t synched = 0;
|
||||
|
||||
struct CertificateEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t entity;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
std::string type;
|
||||
std::string status;
|
||||
std::string certificate;
|
||||
std::string key;
|
||||
std::string devid;
|
||||
std::string cas;
|
||||
std::string manufacturer;
|
||||
std::string model;
|
||||
std::string redirector;
|
||||
std::string commonName;
|
||||
std::string certificateId;
|
||||
OpenWifi::Types::UUID_t batch;
|
||||
uint64_t created = 0;
|
||||
uint64_t modified = 0;
|
||||
uint64_t revoked = 0;
|
||||
uint64_t revokeCount = 0;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
struct EntityEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string defaultRedirector;
|
||||
std::string apiKey;
|
||||
std::string serverEnrollmentProfile;
|
||||
std::string clientEnrollmentProfile;
|
||||
std::string organization;
|
||||
SecurityObjects::NoteInfoVec notes;
|
||||
bool suspended=false;
|
||||
bool deleted=false;
|
||||
uint64_t created = 0 ;
|
||||
uint64_t modified = 0 ;
|
||||
|
||||
struct EntityEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string defaultRedirector;
|
||||
std::string apiKey;
|
||||
std::string serverEnrollmentProfile;
|
||||
std::string clientEnrollmentProfile;
|
||||
std::string organization;
|
||||
SecurityObjects::NoteInfoVec notes;
|
||||
bool suspended=false;
|
||||
bool deleted=false;
|
||||
uint64_t created = 0 ;
|
||||
uint64_t modified = 0 ;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
struct BatchEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t entity;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string manufacturer;
|
||||
std::string model;
|
||||
std::string redirector;
|
||||
std::vector<std::string> commonNames;
|
||||
std::vector<std::string> jobHistory;
|
||||
SecurityObjects::NoteInfoVec notes;
|
||||
uint64_t submitted = 0 ;
|
||||
uint64_t started = 0 ;
|
||||
uint64_t completed = 0 ;
|
||||
uint64_t modified = 0 ;
|
||||
|
||||
struct BatchEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t entity;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string manufacturer;
|
||||
std::string model;
|
||||
std::string redirector;
|
||||
std::vector<std::string> commonNames;
|
||||
std::vector<std::string> jobHistory;
|
||||
SecurityObjects::NoteInfoVec notes;
|
||||
uint64_t submitted = 0 ;
|
||||
uint64_t started = 0 ;
|
||||
uint64_t completed = 0 ;
|
||||
uint64_t modified = 0 ;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
struct JobEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t entity;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
OpenWifi::Types::UUID_t batch;
|
||||
std::string command;
|
||||
OpenWifi::Types::StringVec commonNames;
|
||||
OpenWifi::Types::StringVec completedNames;
|
||||
OpenWifi::Types::StringVec errorNames;
|
||||
Types::StringPairVec parameters;
|
||||
std::string status;
|
||||
uint64_t submitted=0;
|
||||
uint64_t started=0;
|
||||
uint64_t completed=0;
|
||||
std::string requesterUsername;
|
||||
|
||||
struct JobEntry {
|
||||
OpenWifi::Types::UUID_t id;
|
||||
OpenWifi::Types::UUID_t entity;
|
||||
OpenWifi::Types::UUID_t creator;
|
||||
OpenWifi::Types::UUID_t batch;
|
||||
std::string command;
|
||||
OpenWifi::Types::StringVec commonNames;
|
||||
OpenWifi::Types::StringVec completedNames;
|
||||
OpenWifi::Types::StringVec errorNames;
|
||||
Types::StringPairVec parameters;
|
||||
std::string status;
|
||||
uint64_t submitted=0;
|
||||
uint64_t started=0;
|
||||
uint64_t completed=0;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct DashBoardYearlyStats {
|
||||
uint64_t year=0;
|
||||
OpenWifi::Types::Counted3DMapSII activeCerts;
|
||||
OpenWifi::Types::Counted3DMapSII revokedCerts;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct Dashboard {
|
||||
uint64_t snapshot=0;
|
||||
uint64_t numberOfIssuedCerts=0;
|
||||
uint64_t numberOfRevokedCerts=0;
|
||||
OpenWifi::Types::CountedMap activeCertsPerOrganization;
|
||||
OpenWifi::Types::CountedMap revokedCertsPerOrganization;
|
||||
OpenWifi::Types::CountedMap numberOfRedirectors;
|
||||
OpenWifi::Types::CountedMap deviceTypes;
|
||||
OpenWifi::Types::CountedMap monthlyNumberOfCerts;
|
||||
std::vector<DashBoardYearlyStats> monthlyNumberOfCertsPerOrgPerYear;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
void reset();
|
||||
};
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,8 @@
|
||||
//
|
||||
|
||||
#include "RESTAPI_FMSObjects.h"
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/RESTAPI_utils.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
using OpenWifi::RESTAPI_utils::field_to_json;
|
||||
using OpenWifi::RESTAPI_utils::field_from_json;
|
||||
@@ -233,7 +234,7 @@ namespace OpenWifi::FMSObjects {
|
||||
UnknownFirmwares_.clear();
|
||||
totalSecondsOld_.clear();
|
||||
numberOfDevices = 0 ;
|
||||
snapshot = OpenWifi::Now();
|
||||
snapshot = Utils::Now();
|
||||
}
|
||||
|
||||
bool DeviceReport::from_json([[maybe_unused]] const Poco::JSON::Object::Ptr &Obj) {
|
||||
|
||||
@@ -11,12 +11,13 @@
|
||||
|
||||
#include "Daemon.h"
|
||||
#ifdef TIP_GATEWAY_SERVICE
|
||||
#include "DeviceRegistry.h"
|
||||
#include "AP_WS_Server.h"
|
||||
#include "CapabilitiesCache.h"
|
||||
#endif
|
||||
|
||||
#include "RESTAPI_GWobjects.h"
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/RESTAPI_utils.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
using OpenWifi::RESTAPI_utils::field_to_json;
|
||||
using OpenWifi::RESTAPI_utils::field_from_json;
|
||||
@@ -49,6 +50,10 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
field_to_json(Obj,"locale", locale);
|
||||
field_to_json(Obj,"restrictedDevice", restrictedDevice);
|
||||
field_to_json(Obj,"pendingConfiguration", pendingConfiguration);
|
||||
field_to_json(Obj,"pendingConfigurationCmd", pendingConfigurationCmd);
|
||||
field_to_json(Obj,"restrictionDetails", restrictionDetails);
|
||||
}
|
||||
|
||||
void Device::to_json_with_status(Poco::JSON::Object &Obj) const {
|
||||
@@ -57,7 +62,7 @@ namespace OpenWifi::GWObjects {
|
||||
#ifdef TIP_GATEWAY_SERVICE
|
||||
ConnectionState ConState;
|
||||
|
||||
if (DeviceRegistry()->GetState(SerialNumber, ConState)) {
|
||||
if (AP_WS_Server()->GetState(SerialNumber, ConState)) {
|
||||
ConState.to_json(Obj);
|
||||
} else {
|
||||
field_to_json(Obj,"ipAddress", "");
|
||||
@@ -69,6 +74,7 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj,"verifiedCertificate", "NO_CERTIFICATE");
|
||||
field_to_json(Obj,"associations_2G", (uint64_t) 0);
|
||||
field_to_json(Obj,"associations_5G", (uint64_t) 0);
|
||||
field_to_json(Obj,"associations_6G", (uint64_t) 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -88,6 +94,10 @@ namespace OpenWifi::GWObjects {
|
||||
field_from_json(Obj,"subscriber", subscriber);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"locale", locale);
|
||||
field_from_json(Obj,"restrictedDevice", restrictedDevice);
|
||||
field_from_json(Obj,"pendingConfiguration", pendingConfiguration);
|
||||
field_from_json(Obj,"pendingConfigurationCmd", pendingConfigurationCmd);
|
||||
field_from_json(Obj,"restrictionDetails", restrictionDetails);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
@@ -198,11 +208,17 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj,"lastContact", LastContact);
|
||||
field_to_json(Obj,"associations_2G", Associations_2G);
|
||||
field_to_json(Obj,"associations_5G", Associations_5G);
|
||||
field_to_json(Obj,"associations_6G", Associations_6G);
|
||||
field_to_json(Obj,"webSocketClients", webSocketClients);
|
||||
field_to_json(Obj,"websocketPackets", websocketPackets);
|
||||
field_to_json(Obj,"kafkaClients", kafkaClients);
|
||||
field_to_json(Obj,"kafkaPackets", kafkaPackets);
|
||||
field_to_json(Obj,"locale", locale);
|
||||
field_to_json(Obj,"started", started);
|
||||
field_to_json(Obj,"sessionId", sessionId);
|
||||
field_to_json(Obj,"connectionCompletionTime", connectionCompletionTime);
|
||||
field_to_json(Obj,"totalConnectionTime", Utils::Now() - started);
|
||||
field_to_json(Obj,"certificateExpiryDate", certificateExpiryDate);
|
||||
|
||||
switch(VerifiedCertificate) {
|
||||
case NO_CERTIFICATE:
|
||||
@@ -218,6 +234,23 @@ namespace OpenWifi::GWObjects {
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceConnectionStatistics::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"averageConnectionTime", averageConnectionTime);
|
||||
field_to_json(Obj,"connectedDevices", connectedDevices );
|
||||
field_to_json(Obj,"connectingDevices", connectingDevices );
|
||||
}
|
||||
|
||||
bool DeviceConnectionStatistics::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"averageConnectionTime", averageConnectionTime);
|
||||
field_from_json(Obj,"connectedDevices", connectedDevices );
|
||||
field_from_json(Obj,"connectingDevices", connectingDevices );
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RttySessionDetails::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"serialNumber", SerialNumber);
|
||||
field_to_json(Obj,"server", Server);
|
||||
@@ -264,7 +297,7 @@ namespace OpenWifi::GWObjects {
|
||||
lastContact.clear();
|
||||
associations.clear();
|
||||
numberOfDevices = 0 ;
|
||||
snapshot = OpenWifi::Now();
|
||||
snapshot = Utils::Now();
|
||||
}
|
||||
|
||||
void CapabilitiesModel::to_json(Poco::JSON::Object &Obj) const{
|
||||
@@ -276,9 +309,12 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj,"serialNumber",serialNumber);
|
||||
field_to_json(Obj,"timeout",timeout);
|
||||
field_to_json(Obj,"type",type);
|
||||
field_to_json(Obj,"script",script);
|
||||
field_to_json(Obj,"scriptId",scriptId);
|
||||
field_to_json(Obj,"script",script);
|
||||
field_to_json(Obj,"when",when);
|
||||
field_to_json(Obj,"signature", signature);
|
||||
field_to_json(Obj,"deferred", deferred);
|
||||
field_to_json(Obj,"uri", uri);
|
||||
}
|
||||
|
||||
bool ScriptRequest::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
@@ -289,11 +325,13 @@ namespace OpenWifi::GWObjects {
|
||||
field_from_json(Obj,"script",script);
|
||||
field_from_json(Obj,"scriptId",scriptId);
|
||||
field_from_json(Obj,"when",when);
|
||||
field_from_json(Obj,"signature", signature);
|
||||
field_from_json(Obj,"deferred", deferred);
|
||||
field_from_json(Obj,"uri", uri);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void RadiusProxyPoolList::to_json(Poco::JSON::Object &Obj) const {
|
||||
@@ -314,6 +352,8 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj,"description",description);
|
||||
field_to_json(Obj,"authConfig",authConfig);
|
||||
field_to_json(Obj,"acctConfig",acctConfig);
|
||||
field_to_json(Obj,"coaConfig",coaConfig);
|
||||
field_to_json(Obj,"useByDefault",useByDefault);
|
||||
}
|
||||
|
||||
bool RadiusProxyPool::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
@@ -322,6 +362,8 @@ namespace OpenWifi::GWObjects {
|
||||
field_from_json(Obj,"description",description);
|
||||
field_from_json(Obj,"authConfig",authConfig);
|
||||
field_from_json(Obj,"acctConfig",acctConfig);
|
||||
field_from_json(Obj,"coaConfig",coaConfig);
|
||||
field_from_json(Obj,"useByDefault",useByDefault);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
@@ -329,7 +371,7 @@ namespace OpenWifi::GWObjects {
|
||||
}
|
||||
|
||||
void RadiusProxyServerConfig::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"policy",strategy);
|
||||
field_to_json(Obj,"strategy",strategy);
|
||||
field_to_json(Obj,"monitor",monitor);
|
||||
field_to_json(Obj,"monitorMethod",monitorMethod);
|
||||
field_to_json(Obj,"methodParameters",methodParameters);
|
||||
@@ -338,7 +380,7 @@ namespace OpenWifi::GWObjects {
|
||||
|
||||
bool RadiusProxyServerConfig::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"policy",strategy);
|
||||
field_from_json(Obj,"strategy",strategy);
|
||||
field_from_json(Obj,"monitor",monitor);
|
||||
field_from_json(Obj,"monitorMethod",monitorMethod);
|
||||
field_from_json(Obj,"methodParameters",methodParameters);
|
||||
@@ -354,6 +396,17 @@ namespace OpenWifi::GWObjects {
|
||||
field_to_json(Obj,"ip",ip);
|
||||
field_to_json(Obj,"port",port);
|
||||
field_to_json(Obj,"weight",weight);
|
||||
field_to_json(Obj,"secret",secret);
|
||||
field_to_json(Obj,"certificate",certificate);
|
||||
field_to_json(Obj,"radsec",radsec);
|
||||
field_to_json(Obj,"allowSelfSigned",allowSelfSigned);
|
||||
field_to_json(Obj,"radsecPort",radsecPort);
|
||||
field_to_json(Obj,"radsecSecret",radsecSecret);
|
||||
field_to_json(Obj,"radsecCacerts",radsecCacerts);
|
||||
field_to_json(Obj,"radsecCert",radsecCert);
|
||||
field_to_json(Obj,"radsecKey",radsecKey);
|
||||
field_to_json(Obj,"radsecRealms",radsecRealms);
|
||||
field_to_json(Obj,"ignore",ignore);
|
||||
}
|
||||
|
||||
bool RadiusProxyServerEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
@@ -362,10 +415,133 @@ namespace OpenWifi::GWObjects {
|
||||
field_from_json(Obj,"ip",ip);
|
||||
field_from_json(Obj,"port",port);
|
||||
field_from_json(Obj,"weight",weight);
|
||||
field_from_json(Obj,"secret",secret);
|
||||
field_from_json(Obj,"certificate",certificate);
|
||||
field_from_json(Obj,"radsec",radsec);
|
||||
field_from_json(Obj,"allowSelfSigned",allowSelfSigned);
|
||||
field_from_json(Obj,"radsecSecret",radsecSecret);
|
||||
field_from_json(Obj,"radsecPort",radsecPort);
|
||||
field_from_json(Obj,"radsecCacerts",radsecCacerts);
|
||||
field_from_json(Obj,"radsecCert",radsecCert);
|
||||
field_from_json(Obj,"radsecKey",radsecKey);
|
||||
field_from_json(Obj,"radsecRealms",radsecRealms);
|
||||
field_from_json(Obj,"ignore",ignore);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"name", name);
|
||||
field_to_json(Obj,"description", description);
|
||||
field_to_json(Obj,"uri", uri);
|
||||
field_to_json(Obj,"content", content);
|
||||
field_to_json(Obj,"version", version);
|
||||
field_to_json(Obj,"type", type);
|
||||
field_to_json(Obj,"created", created);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
field_to_json(Obj,"author", author);
|
||||
field_to_json(Obj,"restricted", restricted);
|
||||
field_to_json(Obj,"deferred", deferred);
|
||||
field_to_json(Obj,"timeout", timeout);
|
||||
field_to_json(Obj,"defaultUploadURI", defaultUploadURI);
|
||||
}
|
||||
|
||||
bool ScriptEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"name", name);
|
||||
field_from_json(Obj,"description", description);
|
||||
field_from_json(Obj,"uri", uri);
|
||||
field_from_json(Obj,"content", content);
|
||||
field_from_json(Obj,"version", version);
|
||||
field_from_json(Obj,"type", type);
|
||||
field_from_json(Obj,"created", created);
|
||||
field_from_json(Obj,"modified", modified);
|
||||
field_from_json(Obj,"author", author);
|
||||
field_from_json(Obj,"restricted", restricted);
|
||||
field_from_json(Obj,"deferred", deferred);
|
||||
field_from_json(Obj,"timeout", timeout);
|
||||
field_from_json(Obj,"defaultUploadURI", defaultUploadURI);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptEntryList::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"scripts",scripts);
|
||||
}
|
||||
|
||||
bool ScriptEntryList::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"scripts",scripts);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DeviceRestrictionsKeyInfo::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"vendor", vendor);
|
||||
field_to_json(Obj,"algo", algo);
|
||||
}
|
||||
|
||||
bool DeviceRestrictionsKeyInfo::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"vendor", vendor);
|
||||
field_from_json(Obj,"algo", algo);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void DeviceRestrictions::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"dfs", dfs);
|
||||
field_to_json(Obj,"ssh", ssh);
|
||||
field_to_json(Obj,"rtty", rtty);
|
||||
field_to_json(Obj,"tty", tty);
|
||||
field_to_json(Obj,"developer", developer);
|
||||
field_to_json(Obj,"upgrade", upgrade);
|
||||
field_to_json(Obj,"commands", commands);
|
||||
field_to_json(Obj,"country", country);
|
||||
field_to_json(Obj,"key_info", key_info);
|
||||
}
|
||||
|
||||
bool DeviceRestrictions::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"dfs", dfs);
|
||||
field_from_json(Obj,"ssh", ssh);
|
||||
field_from_json(Obj,"rtty", rtty);
|
||||
field_from_json(Obj,"tty", tty);
|
||||
field_from_json(Obj,"developer", developer);
|
||||
field_from_json(Obj,"upgrade", upgrade);
|
||||
field_from_json(Obj,"commands", commands);
|
||||
field_from_json(Obj,"country", country);
|
||||
field_from_json(Obj,"key_info", key_info);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DeviceRestrictionsKeyInfo::operator!=(const OpenWifi::GWObjects::DeviceRestrictionsKeyInfo &T) const {
|
||||
return (T.algo!=algo) || (T.vendor!=vendor);
|
||||
}
|
||||
|
||||
bool DeviceRestrictions::operator!=(const OpenWifi::GWObjects::DeviceRestrictions &T) const {
|
||||
return ( (T.dfs!=dfs) ||
|
||||
(T.rtty!=rtty) ||
|
||||
(T.upgrade!=upgrade) ||
|
||||
(T.commands != commands) ||
|
||||
(T.developer != developer) ||
|
||||
(T.ssh !=ssh) ||
|
||||
(T.key_info != key_info) ||
|
||||
(T.country != country) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,19 +28,52 @@ namespace OpenWifi::GWObjects {
|
||||
uint64_t TX = 0, RX = 0;
|
||||
uint64_t Associations_2G=0;
|
||||
uint64_t Associations_5G=0;
|
||||
uint64_t Associations_6G=0;
|
||||
bool Connected = false;
|
||||
uint64_t LastContact=0;
|
||||
std::string Firmware;
|
||||
CertificateValidation VerifiedCertificate = NO_CERTIFICATE;
|
||||
std::string Compatible;
|
||||
uint64_t kafkaClients=0;
|
||||
uint64_t webSocketClients=0;
|
||||
uint64_t kafkaPackets=0;
|
||||
uint64_t websocketPackets=0;
|
||||
std::string locale;
|
||||
std::string Compatible;
|
||||
uint64_t kafkaClients=0;
|
||||
uint64_t webSocketClients=0;
|
||||
uint64_t kafkaPackets=0;
|
||||
uint64_t websocketPackets=0;
|
||||
std::string locale;
|
||||
uint64_t started=0;
|
||||
uint64_t sessionId=0;
|
||||
double connectionCompletionTime=0.0;
|
||||
std::uint64_t certificateExpiryDate=0;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct DeviceRestrictionsKeyInfo {
|
||||
std::string vendor;
|
||||
std::string algo;
|
||||
|
||||
bool operator !=(const DeviceRestrictionsKeyInfo &b) const;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct DeviceRestrictions {
|
||||
bool dfs = false;
|
||||
bool ssh = false;
|
||||
bool rtty = false;
|
||||
bool tty = false;
|
||||
bool developer = false;
|
||||
bool upgrade = false;
|
||||
bool commands = false;
|
||||
std::vector<std::string> country;
|
||||
DeviceRestrictionsKeyInfo key_info;
|
||||
|
||||
bool operator !=(const DeviceRestrictions &D) const;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct Device {
|
||||
std::string SerialNumber;
|
||||
std::string DeviceType;
|
||||
@@ -64,6 +97,10 @@ namespace OpenWifi::GWObjects {
|
||||
std::string entity;
|
||||
uint64_t modified=0;
|
||||
std::string locale;
|
||||
bool restrictedDevice=false;
|
||||
std::string pendingConfiguration;
|
||||
std::string pendingConfigurationCmd;
|
||||
DeviceRestrictions restrictionDetails;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
void to_json_with_status(Poco::JSON::Object &Obj) const;
|
||||
@@ -71,6 +108,15 @@ namespace OpenWifi::GWObjects {
|
||||
void Print() const;
|
||||
};
|
||||
|
||||
struct DeviceConnectionStatistics {
|
||||
std::uint64_t connectedDevices = 0;
|
||||
std::uint64_t averageConnectionTime = 0;
|
||||
std::uint64_t connectingDevices = 0;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct Statistics {
|
||||
std::string SerialNumber;
|
||||
uint64_t UUID = 0 ;
|
||||
@@ -200,13 +246,44 @@ namespace OpenWifi::GWObjects {
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct ScriptEntry {
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string uri;
|
||||
std::string content;
|
||||
std::string version;
|
||||
std::string type;
|
||||
std::uint64_t created;
|
||||
std::uint64_t modified;
|
||||
std::string author;
|
||||
Types::StringVec restricted;
|
||||
bool deferred=false;
|
||||
std::uint64_t timeout=30;
|
||||
std::string defaultUploadURI;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct ScriptEntryList {
|
||||
std::vector<ScriptEntry> scripts;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct ScriptRequest {
|
||||
uint64_t timeout=30;
|
||||
std::string serialNumber;
|
||||
uint64_t timeout=30;
|
||||
std::string type;
|
||||
std::string script;
|
||||
std::string scriptId;
|
||||
uint64_t when=0;
|
||||
std::uint64_t when;
|
||||
std::string signature;
|
||||
bool deferred;
|
||||
std::string uri;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
@@ -216,6 +293,17 @@ namespace OpenWifi::GWObjects {
|
||||
std::string ip;
|
||||
uint16_t port=0;
|
||||
uint64_t weight=0;
|
||||
std::string secret;
|
||||
std::string certificate;
|
||||
bool radsec=false;
|
||||
bool allowSelfSigned=false;
|
||||
uint16_t radsecPort=2083;
|
||||
std::string radsecSecret;
|
||||
std::string radsecKey;
|
||||
std::string radsecCert;
|
||||
std::vector<std::string> radsecCacerts;
|
||||
std::vector<std::string> radsecRealms;
|
||||
bool ignore=false;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
@@ -237,6 +325,8 @@ namespace OpenWifi::GWObjects {
|
||||
std::string description;
|
||||
RadiusProxyServerConfig authConfig;
|
||||
RadiusProxyServerConfig acctConfig;
|
||||
RadiusProxyServerConfig coaConfig;
|
||||
bool useByDefault=false;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
@@ -248,4 +338,5 @@ namespace OpenWifi::GWObjects {
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
110
src/RESTObjects/RESTAPI_OWLSobjects.cpp
Normal file
110
src/RESTObjects/RESTAPI_OWLSobjects.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-08-31.
|
||||
//
|
||||
|
||||
#include "framework/RESTAPI_utils.h"
|
||||
|
||||
using OpenWifi::RESTAPI_utils::field_to_json;
|
||||
using OpenWifi::RESTAPI_utils::field_from_json;
|
||||
using OpenWifi::RESTAPI_utils::EmbedDocument;
|
||||
|
||||
#include "RESTAPI_OWLSobjects.h"
|
||||
|
||||
// SIM -> 0x53/0x073, 0x49/0x69, 0x4d/0x6d
|
||||
|
||||
namespace OpenWifi::OWLSObjects {
|
||||
|
||||
void SimulationDetails::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"name", name);
|
||||
field_to_json(Obj,"gateway", gateway);
|
||||
field_to_json(Obj,"certificate", certificate);
|
||||
field_to_json(Obj,"key", key);
|
||||
field_to_json(Obj,"macPrefix", macPrefix);
|
||||
field_to_json(Obj,"deviceType", deviceType);
|
||||
field_to_json(Obj,"devices", devices);
|
||||
field_to_json(Obj,"healthCheckInterval", healthCheckInterval);
|
||||
field_to_json(Obj,"stateInterval", stateInterval);
|
||||
field_to_json(Obj,"minAssociations", minAssociations);
|
||||
field_to_json(Obj,"maxAssociations", maxAssociations);
|
||||
field_to_json(Obj,"minClients", minClients);
|
||||
field_to_json(Obj,"maxClients", maxClients);
|
||||
field_to_json(Obj,"simulationLength", simulationLength);
|
||||
field_to_json(Obj,"threads", threads);
|
||||
field_to_json(Obj,"clientInterval", clientInterval);
|
||||
field_to_json(Obj,"keepAlive", keepAlive);
|
||||
field_to_json(Obj,"reconnectInterval", reconnectInterval);
|
||||
field_to_json(Obj,"concurrentDevices", concurrentDevices);
|
||||
}
|
||||
|
||||
bool SimulationDetails::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"id", id);
|
||||
field_from_json(Obj,"name", name);
|
||||
field_from_json(Obj,"gateway", gateway);
|
||||
field_from_json(Obj,"certificate", certificate);
|
||||
field_from_json(Obj,"key", key);
|
||||
field_from_json(Obj,"macPrefix", macPrefix);
|
||||
field_from_json(Obj,"deviceType", deviceType);
|
||||
field_from_json(Obj,"devices", devices);
|
||||
field_from_json(Obj,"healthCheckInterval", healthCheckInterval);
|
||||
field_from_json(Obj,"stateInterval", stateInterval);
|
||||
field_from_json(Obj,"minAssociations", minAssociations);
|
||||
field_from_json(Obj,"maxAssociations", maxAssociations);
|
||||
field_from_json(Obj,"minClients", minClients);
|
||||
field_from_json(Obj,"maxClients", maxClients);
|
||||
field_from_json(Obj,"simulationLength", simulationLength);
|
||||
field_from_json(Obj,"threads", threads);
|
||||
field_from_json(Obj,"clientInterval", clientInterval);
|
||||
field_from_json(Obj,"keepAlive", keepAlive);
|
||||
field_from_json(Obj,"reconnectInterval", reconnectInterval);
|
||||
field_from_json(Obj,"concurrentDevices", concurrentDevices);
|
||||
return true;
|
||||
} catch(...) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SimulationDetailsList::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"list", list);
|
||||
}
|
||||
|
||||
bool SimulationDetailsList::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"list", list);
|
||||
return true;
|
||||
} catch(...) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SimulationStatus::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"id", id);
|
||||
field_to_json(Obj,"simulationId", simulationId);
|
||||
field_to_json(Obj,"state", state);
|
||||
field_to_json(Obj,"tx", tx);
|
||||
field_to_json(Obj,"rx", rx);
|
||||
field_to_json(Obj,"msgsTx", msgsTx);
|
||||
field_to_json(Obj,"msgsRx", msgsRx);
|
||||
field_to_json(Obj,"liveDevices", liveDevices);
|
||||
field_to_json(Obj,"timeToFullDevices", timeToFullDevices);
|
||||
field_to_json(Obj,"startTime", startTime);
|
||||
field_to_json(Obj,"endTime", endTime);
|
||||
field_to_json(Obj,"errorDevices", errorDevices);
|
||||
field_to_json(Obj,"owner", owner);
|
||||
}
|
||||
|
||||
void Dashboard::to_json([[maybe_unused]] Poco::JSON::Object &Obj) const {
|
||||
|
||||
}
|
||||
|
||||
bool Dashboard::from_json([[maybe_unused]] const Poco::JSON::Object::Ptr &Obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Dashboard::reset() {
|
||||
|
||||
}
|
||||
}
|
||||
77
src/RESTObjects/RESTAPI_OWLSobjects.h
Normal file
77
src/RESTObjects/RESTAPI_OWLSobjects.h
Normal file
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// Created by stephane bourque on 2021-08-31.
|
||||
//
|
||||
|
||||
#ifndef UCENTRALSIM_RESTAPI_OWLSOBJECTS_H
|
||||
#define UCENTRALSIM_RESTAPI_OWLSOBJECTS_H
|
||||
|
||||
#include <vector>
|
||||
#include "Poco/JSON/Object.h"
|
||||
|
||||
namespace OpenWifi::OWLSObjects {
|
||||
|
||||
struct SimulationDetails {
|
||||
std::string id;
|
||||
std::string name;
|
||||
std::string gateway;
|
||||
std::string certificate;
|
||||
std::string key;
|
||||
std::string macPrefix;
|
||||
std::string deviceType;
|
||||
uint64_t devices = 5;
|
||||
uint64_t healthCheckInterval = 60;
|
||||
uint64_t stateInterval = 60 ;
|
||||
uint64_t minAssociations = 1;
|
||||
uint64_t maxAssociations = 3;
|
||||
uint64_t minClients = 1 ;
|
||||
uint64_t maxClients = 3;
|
||||
uint64_t simulationLength = 60 * 60;
|
||||
uint64_t threads = 16;
|
||||
uint64_t clientInterval = 1;
|
||||
uint64_t keepAlive = 300;
|
||||
uint64_t reconnectInterval = 30 ;
|
||||
uint64_t concurrentDevices = 5;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct SimulationDetailsList {
|
||||
std::vector<SimulationDetails> list;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct SimulationStatus {
|
||||
std::string id;
|
||||
std::string simulationId;
|
||||
std::string state;
|
||||
uint64_t tx;
|
||||
uint64_t rx;
|
||||
uint64_t msgsTx;
|
||||
uint64_t msgsRx;
|
||||
uint64_t liveDevices;
|
||||
uint64_t timeToFullDevices;
|
||||
uint64_t startTime;
|
||||
uint64_t endTime;
|
||||
uint64_t errorDevices;
|
||||
std::string owner;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
|
||||
struct Dashboard {
|
||||
int O;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
void reset();
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //UCENTRALSIM_RESTAPI_OWLSOBJECTS_H
|
||||
@@ -8,7 +8,9 @@
|
||||
|
||||
|
||||
#include "RESTAPI_ProvObjects.h"
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/RESTAPI_utils.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
using OpenWifi::RESTAPI_utils::field_to_json;
|
||||
using OpenWifi::RESTAPI_utils::field_from_json;
|
||||
@@ -600,6 +602,7 @@ namespace OpenWifi::ProvObjects {
|
||||
field_to_json( Obj, "devClass",devClass);
|
||||
field_to_json( Obj, "locale",locale);
|
||||
field_to_json( Obj, "realMacAddress",realMacAddress);
|
||||
field_to_json( Obj, "doNotAllowOverrides",doNotAllowOverrides);
|
||||
}
|
||||
|
||||
bool InventoryTag::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
@@ -621,6 +624,7 @@ namespace OpenWifi::ProvObjects {
|
||||
field_from_json( Obj,"devClass",devClass);
|
||||
field_from_json( Obj,"locale",locale);
|
||||
field_from_json( Obj,"realMacAddress",realMacAddress);
|
||||
field_from_json( Obj, "doNotAllowOverrides",doNotAllowOverrides);
|
||||
return true;
|
||||
} catch(...) {
|
||||
|
||||
@@ -1091,7 +1095,7 @@ namespace OpenWifi::ProvObjects {
|
||||
}
|
||||
|
||||
bool UpdateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I) {
|
||||
uint64_t Now = OpenWifi::Now();
|
||||
uint64_t Now = Utils::Now();
|
||||
if(O->has("name"))
|
||||
I.name = O->get("name").toString();
|
||||
|
||||
@@ -1112,7 +1116,7 @@ namespace OpenWifi::ProvObjects {
|
||||
}
|
||||
|
||||
bool CreateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I) {
|
||||
uint64_t Now = OpenWifi::Now();
|
||||
uint64_t Now = Utils::Now();
|
||||
if(O->has("name"))
|
||||
I.name = O->get("name").toString();
|
||||
|
||||
@@ -1130,14 +1134,14 @@ namespace OpenWifi::ProvObjects {
|
||||
}
|
||||
I.notes = N;
|
||||
I.modified = I.created = Now;
|
||||
I.id = MicroService::CreateUUID();
|
||||
I.id = MicroServiceCreateUUID();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CreateObjectInfo([[maybe_unused]] const SecurityObjects::UserInfo &U, ObjectInfo &I) {
|
||||
I.modified = I.created = OpenWifi::Now();
|
||||
I.id = MicroService::CreateUUID();
|
||||
I.modified = I.created = Utils::Now();
|
||||
I.id = MicroServiceCreateUUID();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1159,5 +1163,82 @@ namespace OpenWifi::ProvObjects {
|
||||
return false;
|
||||
}
|
||||
|
||||
void RRMAlgorithmDetails::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"name",name);
|
||||
field_to_json(Obj,"parameters",parameters);
|
||||
}
|
||||
|
||||
bool RRMAlgorithmDetails::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"name",name);
|
||||
field_from_json(Obj,"parameters",parameters);
|
||||
return true;
|
||||
} catch(...) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RRMDetails::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"vendor",vendor);
|
||||
field_to_json(Obj,"schedule",schedule);
|
||||
field_to_json(Obj,"algorithms",algorithms);
|
||||
}
|
||||
|
||||
bool RRMDetails::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"vendor",vendor);
|
||||
field_from_json(Obj,"schedule",schedule);
|
||||
field_from_json(Obj,"algorithms",algorithms);
|
||||
return true;
|
||||
} catch(...) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConfigurationOverride::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"source",source);
|
||||
field_to_json(Obj,"reason",reason);
|
||||
field_to_json(Obj,"parameterName",parameterName);
|
||||
field_to_json(Obj,"parameterType",parameterType);
|
||||
field_to_json(Obj,"parameterValue",parameterValue);
|
||||
field_to_json(Obj,"modified",modified);
|
||||
}
|
||||
|
||||
bool ConfigurationOverride::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"source",source);
|
||||
field_from_json(Obj,"reason",reason);
|
||||
field_from_json(Obj,"parameterName",parameterName);
|
||||
field_from_json(Obj,"parameterType",parameterType);
|
||||
field_from_json(Obj,"parameterValue",parameterValue);
|
||||
field_from_json(Obj,"modified",modified);
|
||||
return true;
|
||||
} catch(...) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConfigurationOverrideList::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"serialNumber",serialNumber);
|
||||
field_to_json(Obj,"managementPolicy",managementPolicy);
|
||||
field_to_json(Obj,"overrides",overrides);
|
||||
}
|
||||
|
||||
bool ConfigurationOverrideList::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"serialNumber",serialNumber);
|
||||
field_from_json(Obj,"managementPolicy",managementPolicy);
|
||||
field_from_json(Obj,"overrides",overrides);
|
||||
return true;
|
||||
} catch(...) {
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "RESTAPI_SecurityObjects.h"
|
||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
|
||||
|
||||
namespace OpenWifi::ProvObjects {
|
||||
|
||||
@@ -62,6 +61,21 @@ namespace OpenWifi::ProvObjects {
|
||||
};
|
||||
typedef std::vector<ManagementPolicy> ManagementPolicyVec;
|
||||
|
||||
struct RRMAlgorithmDetails {
|
||||
std::string name;
|
||||
std::string parameters;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct RRMDetails {
|
||||
std::string vendor;
|
||||
std::string schedule;
|
||||
std::vector<RRMAlgorithmDetails> algorithms;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct DeviceRules {
|
||||
std::string rcOnly{"inherit"};
|
||||
std::string rrm{"inherit"};
|
||||
@@ -414,6 +428,7 @@ namespace OpenWifi::ProvObjects {
|
||||
std::string devClass;
|
||||
std::string locale;
|
||||
std::string realMacAddress;
|
||||
bool doNotAllowOverrides=false;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
@@ -679,6 +694,27 @@ namespace OpenWifi::ProvObjects {
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct ConfigurationOverride {
|
||||
std::string source;
|
||||
std::string reason;
|
||||
std::string parameterName;
|
||||
std::string parameterType;
|
||||
std::string parameterValue;
|
||||
std::uint64_t modified;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct ConfigurationOverrideList {
|
||||
std::string serialNumber;
|
||||
Types::UUID_t managementPolicy;
|
||||
std::vector<ConfigurationOverride> overrides;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
bool UpdateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I);
|
||||
bool CreateObjectInfo(const Poco::JSON::Object::Ptr &O, const SecurityObjects::UserInfo &U, ObjectInfo &I);
|
||||
bool CreateObjectInfo(const SecurityObjects::UserInfo &U, ObjectInfo &I);
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
#include "Poco/JSON/Parser.h"
|
||||
#include "Poco/JSON/Stringifier.h"
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/RESTAPI_utils.h"
|
||||
#include "RESTAPI_SecurityObjects.h"
|
||||
|
||||
using OpenWifi::RESTAPI_utils::field_to_json;
|
||||
@@ -433,7 +433,7 @@ namespace OpenWifi::SecurityObjects {
|
||||
SecurityObjects::NoteInfoVec NIV;
|
||||
NIV = RESTAPI_utils::to_object_array<SecurityObjects::NoteInfo>(Obj->get("notes").toString());
|
||||
for(auto const &i:NIV) {
|
||||
SecurityObjects::NoteInfo ii{.created=(uint64_t)OpenWifi::Now(), .createdBy=UInfo.email, .note=i.note};
|
||||
SecurityObjects::NoteInfo ii{.created=(uint64_t)Utils::Now(), .createdBy=UInfo.email, .note=i.note};
|
||||
Notes.push_back(ii);
|
||||
}
|
||||
}
|
||||
@@ -446,7 +446,7 @@ namespace OpenWifi::SecurityObjects {
|
||||
|
||||
bool MergeNotes(const NoteInfoVec & NewNotes, const UserInfo &UInfo, NoteInfoVec & ExistingNotes) {
|
||||
for(auto const &i:NewNotes) {
|
||||
SecurityObjects::NoteInfo ii{.created=(uint64_t)OpenWifi::Now(), .createdBy=UInfo.email, .note=i.note};
|
||||
SecurityObjects::NoteInfo ii{.created=(uint64_t)Utils::Now(), .createdBy=UInfo.email, .note=i.note};
|
||||
ExistingNotes.push_back(ii);
|
||||
}
|
||||
return true;
|
||||
@@ -619,5 +619,80 @@ namespace OpenWifi::SecurityObjects {
|
||||
field_to_json(Obj,"login",login);
|
||||
field_to_json(Obj,"logout",logout);
|
||||
}
|
||||
|
||||
void ApiKeyAccessRight::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "service", service);
|
||||
field_to_json(Obj, "access", access);
|
||||
}
|
||||
|
||||
bool ApiKeyAccessRight::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj, "service", service);
|
||||
field_from_json(Obj, "access", access);
|
||||
return true;
|
||||
} catch(...) {
|
||||
std::cout << "Cannot parse: Token" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ApiKeyAccessRightList::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "acls", acls);
|
||||
}
|
||||
|
||||
bool ApiKeyAccessRightList::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj, "acls", acls);
|
||||
return true;
|
||||
} catch(...) {
|
||||
std::cout << "Cannot parse: Token" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ApiKeyEntry::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "id", id);
|
||||
field_to_json(Obj, "userUuid", userUuid);
|
||||
field_to_json(Obj, "name", name);
|
||||
field_to_json(Obj, "apiKey", apiKey);
|
||||
field_to_json(Obj, "salt", salt);
|
||||
field_to_json(Obj, "description", description);
|
||||
field_to_json(Obj, "expiresOn", expiresOn);
|
||||
field_to_json(Obj, "rights", rights);
|
||||
field_to_json(Obj, "lastUse", lastUse);
|
||||
}
|
||||
|
||||
bool ApiKeyEntry::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj, "id", id);
|
||||
field_from_json(Obj, "userUuid", userUuid);
|
||||
field_from_json(Obj, "name", name);
|
||||
field_from_json(Obj, "apiKey", apiKey);
|
||||
field_from_json(Obj, "salt", salt);
|
||||
field_from_json(Obj, "description", description);
|
||||
field_from_json(Obj, "expiresOn", expiresOn);
|
||||
field_from_json(Obj, "rights", rights);
|
||||
field_from_json(Obj, "lastUse", lastUse);
|
||||
return true;
|
||||
} catch(...) {
|
||||
std::cout << "Cannot parse: Token" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ApiKeyEntryList::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj, "apiKeys", apiKeys);
|
||||
}
|
||||
|
||||
bool ApiKeyEntryList::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj, "apiKeys", apiKeys);
|
||||
return true;
|
||||
} catch(...) {
|
||||
std::cout << "Cannot parse: Token" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -9,10 +9,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "Poco/Data/LOB.h"
|
||||
#include "Poco/Data/LOBStream.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
uint64_t Now();
|
||||
@@ -27,8 +29,13 @@ namespace OpenWifi {
|
||||
bool Delete_ = true;
|
||||
bool PortalLogin_ = true;
|
||||
|
||||
AclTemplate() noexcept = default;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj); };
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
static_assert( std::is_nothrow_move_constructible_v<AclTemplate> );
|
||||
|
||||
struct WebToken {
|
||||
std::string access_token_;
|
||||
@@ -56,7 +63,7 @@ namespace OpenWifi {
|
||||
std::string UserTypeToString(USER_ROLE U);
|
||||
|
||||
struct NoteInfo {
|
||||
uint64_t created=0; // = OpenWifi::Now();
|
||||
uint64_t created=0; // = Utils::Now();
|
||||
std::string createdBy;
|
||||
std::string note;
|
||||
|
||||
@@ -95,7 +102,7 @@ namespace OpenWifi {
|
||||
std::string uuid;
|
||||
std::string question;
|
||||
std::string method;
|
||||
uint64_t created = OpenWifi::Now();
|
||||
uint64_t created = Utils::Now();
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
@@ -245,7 +252,8 @@ namespace OpenWifi {
|
||||
VERIFY_EMAIL,
|
||||
SUB_FORGOT_PASSWORD,
|
||||
SUB_VERIFY_EMAIL,
|
||||
SUB_SIGNUP
|
||||
SUB_SIGNUP,
|
||||
EMAIL_INVITATION
|
||||
};
|
||||
|
||||
struct ActionLink {
|
||||
@@ -257,7 +265,7 @@ namespace OpenWifi {
|
||||
std::string locale;
|
||||
std::string message;
|
||||
uint64_t sent=0;
|
||||
uint64_t created=OpenWifi::Now();
|
||||
uint64_t created=Utils::Now();
|
||||
uint64_t expires=0;
|
||||
uint64_t completed=0;
|
||||
uint64_t canceled=0;
|
||||
@@ -317,5 +325,44 @@ namespace OpenWifi {
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct ApiKeyAccessRight {
|
||||
std::string service;
|
||||
std::string access;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct ApiKeyAccessRightList {
|
||||
std::vector<ApiKeyAccessRight> acls;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct ApiKeyEntry {
|
||||
Types::UUID_t id;
|
||||
Types::UUID_t userUuid;
|
||||
std::string name;
|
||||
std::string description;
|
||||
std::string apiKey;
|
||||
std::string salt;
|
||||
std::uint64_t created;
|
||||
std::uint64_t expiresOn=0;
|
||||
ApiKeyAccessRightList rights;
|
||||
std::uint64_t lastUse=0;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct ApiKeyEntryList {
|
||||
std::vector<ApiKeyEntry> apiKeys;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
//
|
||||
|
||||
#include "RESTAPI_SubObjects.h"
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/RESTAPI_utils.h"
|
||||
|
||||
using OpenWifi::RESTAPI_utils::field_to_json;
|
||||
using OpenWifi::RESTAPI_utils::field_from_json;
|
||||
|
||||
|
||||
namespace OpenWifi::SubObjects {
|
||||
|
||||
void HomeDeviceMode::to_json(Poco::JSON::Object &Obj) const {
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include "StateReceiver.h"
|
||||
#include "VenueWatcher.h"
|
||||
#include "fmt/core.h"
|
||||
#include "framework/KafkaTopics.h"
|
||||
#include "framework/KafkaManager.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
@@ -25,6 +27,7 @@ namespace OpenWifi {
|
||||
|
||||
void StateReceiver::run() {
|
||||
Poco::AutoPtr<Poco::Notification> Note(Queue_.waitDequeueNotification());
|
||||
Utils::SetThreadName("dev-state");
|
||||
while(Note && Running_) {
|
||||
auto Msg = dynamic_cast<StateMessage *>(Note.get());
|
||||
if(Msg!= nullptr) {
|
||||
@@ -58,7 +61,7 @@ namespace OpenWifi {
|
||||
|
||||
void StateReceiver::StateReceived( const std::string & Key, const std::string & Payload) {
|
||||
std::lock_guard G(Mutex_);
|
||||
Logger().information(fmt::format("Device({}): State message.", Key));
|
||||
poco_debug(Logger(),fmt::format("Device({}): State message.", Key));
|
||||
Queue_.enqueueNotification( new StateMessage(Key,Payload));
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "Poco/Notification.h"
|
||||
#include "Poco/NotificationQueue.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class StateMessage : public Poco::Notification {
|
||||
|
||||
@@ -8,10 +8,14 @@
|
||||
|
||||
#include "StorageService.h"
|
||||
#include "RESTObjects/RESTAPI_ProvObjects.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
#include "fmt/format.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
int Storage::Start() {
|
||||
poco_notice(Logger(),"Starting...");
|
||||
std::lock_guard Guard(Mutex_);
|
||||
|
||||
StorageClass::Start();
|
||||
@@ -24,7 +28,7 @@ namespace OpenWifi {
|
||||
BoardsDB_->Create();
|
||||
WifiClientHistoryDB_->Create();
|
||||
|
||||
PeriodicCleanup_ = MicroService::instance().ConfigGetInt("storage.cleanup.interval", 6*60*60);
|
||||
PeriodicCleanup_ = MicroServiceConfigGetInt("storage.cleanup.interval", 6*60*60);
|
||||
if(PeriodicCleanup_<1*60*60)
|
||||
PeriodicCleanup_ = 1*60*60;
|
||||
|
||||
@@ -43,14 +47,14 @@ namespace OpenWifi {
|
||||
uint64_t start = 0 ;
|
||||
bool done = false;
|
||||
const uint64_t batch=100;
|
||||
Logger().information("Starting cleanup of TimePoint Database");
|
||||
poco_information(Logger(),"Starting cleanup of TimePoint Database");
|
||||
while(!done) {
|
||||
if(!BoardsDB().GetRecords(start,batch,BoardList)) {
|
||||
for(const auto &board:BoardList) {
|
||||
for(const auto &venue:board.venueList) {
|
||||
auto now = OpenWifi::Now();
|
||||
auto now = Utils::Now();
|
||||
auto lower_bound = now - venue.retention;
|
||||
Logger().information(fmt::format("Removing old records for board '{}'",board.info.name));
|
||||
poco_information(Logger(),fmt::format("Removing old records for board '{}'",board.info.name));
|
||||
BoardsDB().DeleteRecords(fmt::format(" boardId='{}' and timestamp<{}", board.info.id, lower_bound));
|
||||
}
|
||||
}
|
||||
@@ -58,14 +62,15 @@ namespace OpenWifi {
|
||||
done = (BoardList.size() < batch);
|
||||
}
|
||||
|
||||
auto MaxDays = MicroService::instance().ConfigGetInt("wificlient.age.limit",14);
|
||||
auto LowerDate = OpenWifi::Now() - (MaxDays*60*60*24);
|
||||
Logger().information(fmt::format("Removing WiFi Clients history older than {} days.", MaxDays));
|
||||
auto MaxDays = MicroServiceConfigGetInt("wificlient.age.limit",14);
|
||||
auto LowerDate = Utils::Now() - (MaxDays*60*60*24);
|
||||
poco_information(Logger(),fmt::format("Removing WiFi Clients history older than {} days.", MaxDays));
|
||||
StorageService()->WifiClientHistoryDB().DeleteRecords(fmt::format(" timestamp<{} ", LowerDate));
|
||||
Logger().information(fmt::format("Done cleanup of databases. Next run in {} seconds.", PeriodicCleanup_));
|
||||
poco_information(Logger(),fmt::format("Done cleanup of databases. Next run in {} seconds.", PeriodicCleanup_));
|
||||
}
|
||||
|
||||
void Storage::run() {
|
||||
Utils::SetThreadName("strg-updtr");
|
||||
Running_ = true ;
|
||||
bool FirstRun=true;
|
||||
long Retry = 2000;
|
||||
@@ -80,11 +85,12 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
void Storage::Stop() {
|
||||
poco_notice(Logger(),"Stopping...");
|
||||
Running_=false;
|
||||
Timer_.stop();
|
||||
Updater_.wakeUp();
|
||||
Updater_.join();
|
||||
Logger().notice("Stopping.");
|
||||
poco_notice(Logger(),"Stopped...");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/StorageClass.h"
|
||||
#include "storage/storage_boards.h"
|
||||
#include "storage/storage_timepoints.h"
|
||||
|
||||
@@ -7,41 +7,63 @@
|
||||
#include "StorageService.h"
|
||||
#include "sdks/SDK_prov.h"
|
||||
#include "fmt/core.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
int VenueCoordinator::Start() {
|
||||
poco_notice(Logger(),"Starting...");
|
||||
GetBoardList();
|
||||
Worker_.start(*this);
|
||||
|
||||
ReconcileTimerCallback_ = std::make_unique<Poco::TimerCallback<VenueCoordinator>>(*this,&VenueCoordinator::onReconcileTimer);
|
||||
ReconcileTimerTimer_.setStartInterval( 3 * 60 * 1000 );
|
||||
ReconcileTimerTimer_.setPeriodicInterval(3 * 60 * 1000); // 1 hours
|
||||
ReconcileTimerTimer_.start(*ReconcileTimerCallback_, MicroServiceTimerPool());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void VenueCoordinator::onReconcileTimer([[maybe_unused]] Poco::Timer &timer) {
|
||||
std::lock_guard G(Mutex_);
|
||||
Utils::SetThreadName("brd-refresh");
|
||||
|
||||
poco_information(Logger(),"Starting to reconcile board information.");
|
||||
for(const auto &[board_id, watcher]:Watchers_) {
|
||||
poco_information(Logger(),fmt::format("Updating: {}", board_id));
|
||||
UpdateBoard(board_id);
|
||||
}
|
||||
poco_information(Logger(),"Finished reconciling board information.");
|
||||
}
|
||||
|
||||
void VenueCoordinator::GetBoardList() {
|
||||
BoardsToWatch_.clear();
|
||||
auto F = [&](const AnalyticsObjects::BoardInfo &B) ->bool {
|
||||
BoardsToWatch_.insert(B);
|
||||
// Logger().information(fmt::format("Starting watch for {}.", B.info.name));
|
||||
// poco_information(Logger(),fmt::format("Starting watch for {}.", B.info.name));
|
||||
return true;
|
||||
};
|
||||
StorageService()->BoardsDB().Iterate(F);
|
||||
}
|
||||
|
||||
void VenueCoordinator::Stop() {
|
||||
poco_notice(Logger(),"Stopping...");
|
||||
Running_=false;
|
||||
Worker_.wakeUp();
|
||||
Worker_.wakeUp();
|
||||
Worker_.join();
|
||||
poco_notice(Logger(),"Stopped...");
|
||||
}
|
||||
|
||||
void VenueCoordinator::run() {
|
||||
Utils::SetThreadName("venue-coord");
|
||||
Running_=true;
|
||||
while(Running_) {
|
||||
Poco::Thread::trySleep(2000);
|
||||
|
||||
Poco::Thread::trySleep(20000);
|
||||
if(!Running_)
|
||||
break;
|
||||
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
GetBoardList();
|
||||
|
||||
if(!BoardsToWatch_.empty()) {
|
||||
@@ -96,7 +118,7 @@ namespace OpenWifi {
|
||||
ExistingBoards_[B.info.id] = Devices;
|
||||
Watchers_[B.info.id] = std::make_shared<VenueWatcher>(B.info.id, B.venueList[0].id, Logger(), Devices);
|
||||
Watchers_[B.info.id]->Start();
|
||||
Logger().information(fmt::format("Started board {} for venue {}", B.info.name,B.venueList[0].id ));
|
||||
poco_information(Logger(),fmt::format("Started board {} for venue {}", B.info.name,B.venueList[0].id ));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -105,7 +127,7 @@ namespace OpenWifi {
|
||||
return false;
|
||||
}
|
||||
|
||||
Logger().information(fmt::format("Could not start board {}",B.info.name));
|
||||
poco_information(Logger(),fmt::format("Could not start board {}",B.info.name));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -119,7 +141,7 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
void VenueCoordinator::ModifyBoard(const std::string &id) {
|
||||
void VenueCoordinator::UpdateBoard(const std::string &id) {
|
||||
AnalyticsObjects::BoardInfo B;
|
||||
if(StorageService()->BoardsDB().GetRecord("id",id,B)) {
|
||||
std::vector<uint64_t> Devices;
|
||||
@@ -130,15 +152,15 @@ namespace OpenWifi {
|
||||
if(it!=ExistingBoards_.end()) {
|
||||
if(it->second!=Devices) {
|
||||
auto it2 = Watchers_.find(id);
|
||||
if(it2!=Watchers_.end())
|
||||
if(it2!=Watchers_.end()) {
|
||||
it2->second->ModifySerialNumbers(Devices);
|
||||
}
|
||||
ExistingBoards_[id] = Devices;
|
||||
Logger().information(fmt::format("Modified board {}",B.info.name));
|
||||
poco_information(Logger(),fmt::format("Modified board {}",B.info.name));
|
||||
} else {
|
||||
Logger().information(fmt::format("No device changes in board {}",B.info.name));
|
||||
poco_information(Logger(),fmt::format("No device changes in board {}",B.info.name));
|
||||
}
|
||||
}
|
||||
Logger().information(fmt::format("Modified board {}",B.info.name));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -147,7 +169,7 @@ namespace OpenWifi {
|
||||
return;
|
||||
}
|
||||
|
||||
Logger().information(fmt::format("Could not modify board {}",B.info.name));
|
||||
poco_information(Logger(),fmt::format("Could not modify board {}",B.info.name));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +185,7 @@ namespace OpenWifi {
|
||||
if(StorageService()->BoardsDB().GetRecord("id",id,B))
|
||||
BoardsToWatch_.insert(B);
|
||||
else
|
||||
Logger().information(fmt::format("Board {} does not seem to exist",id));
|
||||
poco_information(Logger(),fmt::format("Board {} does not seem to exist",id));
|
||||
}
|
||||
|
||||
void VenueCoordinator::GetDevices(std::string &id, AnalyticsObjects::DeviceInfoList &DIL) {
|
||||
|
||||
@@ -3,9 +3,12 @@
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#include "framework/MicroService.h"
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "VenueWatcher.h"
|
||||
|
||||
#include "Poco/Timer.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class VenueCoordinator : public SubSystemServer, Poco::Runnable {
|
||||
@@ -21,7 +24,7 @@ namespace OpenWifi {
|
||||
void run() override;
|
||||
|
||||
void StopBoard(const std::string &id);
|
||||
void ModifyBoard(const std::string &id);
|
||||
void UpdateBoard(const std::string &id);
|
||||
void AddBoard(const std::string &id);
|
||||
|
||||
bool GetDevicesForBoard(const AnalyticsObjects::BoardInfo &B, std::vector<uint64_t> & Devices, bool & VenueExists);
|
||||
@@ -30,13 +33,17 @@ namespace OpenWifi {
|
||||
bool Watching(const std::string &id);
|
||||
void RetireBoard(const AnalyticsObjects::BoardInfo &B);
|
||||
|
||||
private:
|
||||
Poco::Thread Worker_;
|
||||
std::atomic_bool Running_=false;
|
||||
std::set<AnalyticsObjects::BoardInfo> BoardsToWatch_;
|
||||
std::map<std::string,std::shared_ptr<VenueWatcher>> Watchers_;
|
||||
void onReconcileTimer(Poco::Timer & timer);
|
||||
|
||||
std::map<std::string,std::vector<uint64_t>> ExistingBoards_;
|
||||
private:
|
||||
Poco::Thread Worker_;
|
||||
std::atomic_bool Running_=false;
|
||||
std::set<AnalyticsObjects::BoardInfo> BoardsToWatch_;
|
||||
std::map<std::string,std::shared_ptr<VenueWatcher>> Watchers_;
|
||||
std::unique_ptr<Poco::TimerCallback<VenueCoordinator>> ReconcileTimerCallback_;
|
||||
Poco::Timer ReconcileTimerTimer_;
|
||||
|
||||
std::map<std::string,std::vector<uint64_t>> ExistingBoards_;
|
||||
|
||||
VenueCoordinator() noexcept:
|
||||
SubSystemServer("VenueCoordinator", "VENUE-COORD", "venue.coordinator")
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace OpenWifi {
|
||||
|
||||
void VenueWatcher::Start() {
|
||||
poco_notice(Logger(),"Starting...");
|
||||
for(const auto &mac:SerialNumbers_) {
|
||||
auto ap = std::make_shared<AP>(mac, venue_id_, boardId_, Logger());
|
||||
APs_[mac ] = ap;
|
||||
@@ -24,6 +25,7 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
void VenueWatcher::Stop() {
|
||||
poco_notice(Logger(),"Stopping...");
|
||||
Running_ = false;
|
||||
Queue_.wakeUpAll();
|
||||
Worker_.join();
|
||||
@@ -31,9 +33,11 @@ namespace OpenWifi {
|
||||
StateReceiver()->DeRegister(i,this);
|
||||
DeviceStatusReceiver()->DeRegister(this);
|
||||
HealthReceiver()->DeRegister(this);
|
||||
poco_notice(Logger(),"Stopped...");
|
||||
}
|
||||
|
||||
void VenueWatcher::run() {
|
||||
Utils::SetThreadName("venue-watch");
|
||||
Running_ = true;
|
||||
Poco::AutoPtr<Poco::Notification> Msg(Queue_.waitDequeueNotification());
|
||||
while(Msg && Running_) {
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "APStats.h"
|
||||
#include "RESTObjects/RESTAPI_AnalyticsObjects.h"
|
||||
#include "Poco/NotificationQueue.h"
|
||||
#include "Poco/Notification.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
@@ -69,9 +71,11 @@ namespace OpenWifi {
|
||||
void GetDevices(std::vector<AnalyticsObjects::DeviceInfo> & DI);
|
||||
|
||||
void GetBandwidth(uint64_t start, uint64_t end, uint64_t interval , AnalyticsObjects::BandwidthAnalysis & BW);
|
||||
|
||||
inline std::string Venue() const {
|
||||
return venue_id_;
|
||||
}
|
||||
private:
|
||||
std::recursive_mutex Mutex_;
|
||||
std::mutex Mutex_;
|
||||
std::string boardId_;
|
||||
std::string venue_id_;
|
||||
Poco::NotificationQueue Queue_;
|
||||
|
||||
@@ -2,14 +2,17 @@
|
||||
// Created by stephane bourque on 2021-08-11.
|
||||
//
|
||||
|
||||
#include "WifiClientCache.h"
|
||||
#include <mutex>
|
||||
|
||||
#include "WifiClientCache.h"
|
||||
#include "StorageService.h"
|
||||
#include "fmt/format.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
int WifiClientCache::Start() {
|
||||
poco_notice(Logger(),"Starting...");
|
||||
TimerCallback_ = std::make_unique<Poco::TimerCallback<WifiClientCache>>(*this,&WifiClientCache::onTimer);
|
||||
Timer_.setStartInterval( 30 * 1000); // first run in 20 seconds
|
||||
Timer_.setPeriodicInterval( 60 * 60 * 1000); // 1 hours
|
||||
@@ -18,7 +21,9 @@ namespace OpenWifi {
|
||||
}
|
||||
|
||||
void WifiClientCache::Stop() {
|
||||
poco_notice(Logger(),"Stopping...");
|
||||
Timer_.stop();
|
||||
poco_notice(Logger(),"Stopped...");
|
||||
}
|
||||
|
||||
void WifiClientCache::onTimer([[maybe_unused]] Poco::Timer & timer) {
|
||||
@@ -124,32 +129,36 @@ namespace OpenWifi {
|
||||
}
|
||||
}
|
||||
|
||||
void WifiClientCache::FindNumbers(const std::string &venue_id, const std::string &S, uint HowMany, std::vector<uint64_t> &A) {
|
||||
void WifiClientCache::FindNumbers(const std::string &venueId, const std::string &SerialNumber, std::uint64_t StartingOffset, std::uint64_t HowMany, std::vector<uint64_t> &A) {
|
||||
std::lock_guard G(Mutex_);
|
||||
|
||||
A.clear();
|
||||
auto VenueIt = Cache_.find(venue_id);
|
||||
auto VenueIt = Cache_.find(venueId);
|
||||
if(VenueIt==Cache_.end())
|
||||
return;
|
||||
|
||||
if(S.empty()) {
|
||||
if(SerialNumber.empty()) {
|
||||
auto Start = VenueIt->second.SNs_.begin();
|
||||
std::uint64_t Offset=0;
|
||||
while(HowMany && Start!=VenueIt->second.SNs_.end()) {
|
||||
A.push_back(*Start);
|
||||
if(Offset>=StartingOffset) {
|
||||
A.push_back(*Start);
|
||||
HowMany--;
|
||||
}
|
||||
Start++;
|
||||
HowMany--;
|
||||
Offset++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (S[0] == '*') {
|
||||
if (SerialNumber[0] == '*') {
|
||||
std::string Reversed;
|
||||
std::copy(rbegin(S), rend(S)-1, std::back_inserter(Reversed));
|
||||
std::copy(rbegin(SerialNumber), rend(SerialNumber)-1, std::back_inserter(Reversed));
|
||||
if(Reversed.empty())
|
||||
return;
|
||||
return ReturnNumbers(Reversed, HowMany, VenueIt->second.Reverse_SNs_, A, true);
|
||||
} else {
|
||||
return ReturnNumbers(S, HowMany, VenueIt->second.SNs_, A, false);
|
||||
return ReturnNumbers(SerialNumber, HowMany, VenueIt->second.SNs_, A, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "Poco/Timer.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
@@ -20,7 +20,7 @@ namespace OpenWifi {
|
||||
void Stop() override;
|
||||
void AddSerialNumber(const std::string &venueId, const std::string &SerialNumber);
|
||||
void DeleteSerialNumber(const std::string &venueId, const std::string &SerialNumber);
|
||||
void FindNumbers(const std::string &venueId, const std::string &SerialNumber, uint HowMany, std::vector<uint64_t> &A);
|
||||
void FindNumbers(const std::string &venueId, const std::string &SerialNumber, std::uint64_t start, std::uint64_t HowMany, std::vector<uint64_t> &A);
|
||||
inline bool NumberExists(const std::string &venueId, uint64_t SerialNumber) {
|
||||
std::lock_guard G(Mutex_);
|
||||
auto It = Cache_.find(venueId);
|
||||
|
||||
77
src/framework/ALBserver.cpp
Normal file
77
src/framework/ALBserver.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#include "ALBserver.h"
|
||||
|
||||
#include "framework/utils.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
void ALBRequestHandler::handleRequest([[maybe_unused]] Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) {
|
||||
Utils::SetThreadName("alb-request");
|
||||
try {
|
||||
if((id_ % 100) == 0) {
|
||||
Logger_.debug(fmt::format("ALB-REQUEST({}): ALB Request {}.", Request.clientAddress().toString(), id_));
|
||||
}
|
||||
Response.setChunkedTransferEncoding(true);
|
||||
Response.setContentType("text/html");
|
||||
Response.setDate(Poco::Timestamp());
|
||||
Response.setStatus(Poco::Net::HTTPResponse::HTTP_OK);
|
||||
Response.setKeepAlive(true);
|
||||
Response.set("Connection", "keep-alive");
|
||||
Response.setVersion(Poco::Net::HTTPMessage::HTTP_1_1);
|
||||
std::ostream &Answer = Response.send();
|
||||
Answer << "process Alive and kicking!";
|
||||
} catch (...) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ALBRequestHandlerFactory::ALBRequestHandlerFactory(Poco::Logger & L):
|
||||
Logger_(L) {
|
||||
}
|
||||
|
||||
ALBRequestHandler* ALBRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest& request) {
|
||||
if (request.getURI() == "/")
|
||||
return new ALBRequestHandler(Logger_, req_id_++);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ALBHealthCheckServer::ALBHealthCheckServer() :
|
||||
SubSystemServer("ALBHealthCheckServer", "ALB-SVR", "alb")
|
||||
{
|
||||
}
|
||||
|
||||
int ALBHealthCheckServer::Start() {
|
||||
if(MicroServiceConfigGetBool("alb.enable",false)) {
|
||||
poco_information(Logger(),"Starting...");
|
||||
Running_=true;
|
||||
Port_ = (int)MicroServiceConfigGetInt("alb.port",15015);
|
||||
Poco::Net::IPAddress Addr(Poco::Net::IPAddress::wildcard(
|
||||
Poco::Net::Socket::supportsIPv6() ? Poco::Net::AddressFamily::IPv6
|
||||
: Poco::Net::AddressFamily::IPv4));
|
||||
Poco::Net::SocketAddress SockAddr(Addr, Port_);
|
||||
Poco::Net::ServerSocket ClientSocket(SockAddr, 64);
|
||||
|
||||
Socket_ = std::make_unique<Poco::Net::ServerSocket>(SockAddr, Port_);
|
||||
auto Params = new Poco::Net::HTTPServerParams;
|
||||
Params->setName("ws:alb");
|
||||
Server_ = std::make_unique<Poco::Net::HTTPServer>(new ALBRequestHandlerFactory(Logger()), *Socket_, Params);
|
||||
Server_->start();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ALBHealthCheckServer::Stop() {
|
||||
poco_information(Logger(),"Stopping...");
|
||||
if(Running_)
|
||||
Server_->stopAll(true);
|
||||
poco_information(Logger(),"Stopped...");
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
63
src/framework/ALBserver.h
Normal file
63
src/framework/ALBserver.h
Normal file
@@ -0,0 +1,63 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
|
||||
#include "Poco/Net/HTTPRequestHandler.h"
|
||||
#include "Poco/Net/HTTPServerRequest.h"
|
||||
#include "Poco/Net/HTTPServerResponse.h"
|
||||
#include "Poco/Net/HTTPRequestHandlerFactory.h"
|
||||
#include "Poco/Net/HTTPServer.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class ALBRequestHandler: public Poco::Net::HTTPRequestHandler {
|
||||
public:
|
||||
explicit ALBRequestHandler(Poco::Logger & L, uint64_t id)
|
||||
: Logger_(L), id_(id) {
|
||||
}
|
||||
|
||||
void handleRequest([[maybe_unused]] Poco::Net::HTTPServerRequest& Request, Poco::Net::HTTPServerResponse& Response) override;
|
||||
|
||||
private:
|
||||
Poco::Logger & Logger_;
|
||||
uint64_t id_;
|
||||
};
|
||||
|
||||
class ALBRequestHandlerFactory: public Poco::Net::HTTPRequestHandlerFactory
|
||||
{
|
||||
public:
|
||||
explicit ALBRequestHandlerFactory(Poco::Logger & L);
|
||||
ALBRequestHandler* createRequestHandler(const Poco::Net::HTTPServerRequest& request) override;
|
||||
|
||||
private:
|
||||
Poco::Logger &Logger_;
|
||||
inline static std::atomic_uint64_t req_id_=1;
|
||||
};
|
||||
|
||||
class ALBHealthCheckServer : public SubSystemServer {
|
||||
public:
|
||||
ALBHealthCheckServer();
|
||||
|
||||
static auto instance() {
|
||||
static auto instance = new ALBHealthCheckServer;
|
||||
return instance;
|
||||
}
|
||||
|
||||
int Start() override;
|
||||
void Stop() override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<Poco::Net::HTTPServer> Server_;
|
||||
std::unique_ptr<Poco::Net::ServerSocket> Socket_;
|
||||
int Port_ = 0;
|
||||
mutable std::atomic_bool Running_=false;
|
||||
};
|
||||
|
||||
inline auto ALBHealthCheckServer() { return ALBHealthCheckServer::instance(); }
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -4,8 +4,14 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "Poco/Logger.h"
|
||||
#include "Poco/JSON/Parser.h"
|
||||
#include "Poco/Net/HTTPServerRequest.h"
|
||||
#include "Poco/Net/HTTPServerResponse.h"
|
||||
#include "Poco/Net/HTTPSClientSession.h"
|
||||
#include "Poco/URI.h"
|
||||
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
inline void API_Proxy( Poco::Logger &Logger,
|
||||
@@ -15,7 +21,7 @@ namespace OpenWifi {
|
||||
const char * PathRewrite,
|
||||
uint64_t msTimeout_ = 10000 ) {
|
||||
try {
|
||||
auto Services = MicroService::instance().GetServices(ServiceType);
|
||||
auto Services = MicroServiceGetServices(ServiceType);
|
||||
for(auto const &Svc:Services) {
|
||||
Poco::URI SourceURI(Request->getURI());
|
||||
Poco::URI DestinationURI(Svc.PrivateEndPoint);
|
||||
@@ -35,7 +41,7 @@ namespace OpenWifi {
|
||||
ProxyRequest.add("Authorization", Request->get("Authorization"));
|
||||
} else {
|
||||
ProxyRequest.add("X-API-KEY", Svc.AccessKey);
|
||||
ProxyRequest.add("X-INTERNAL-NAME", MicroService::instance().PublicEndPoint());
|
||||
ProxyRequest.add("X-INTERNAL-NAME", MicroServicePublicEndPoint());
|
||||
}
|
||||
|
||||
if(Request->getMethod() == Poco::Net::HTTPRequest::HTTP_DELETE) {
|
||||
|
||||
102
src/framework/AppServiceRegistry.h
Normal file
102
src/framework/AppServiceRegistry.h
Normal file
@@ -0,0 +1,102 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
#include "Poco/StreamCopier.h"
|
||||
#include "Poco/File.h"
|
||||
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
|
||||
class AppServiceRegistry {
|
||||
public:
|
||||
AppServiceRegistry() {
|
||||
FileName = MicroServiceDataDirectory() + "/registry.json";
|
||||
Poco::File F(FileName);
|
||||
|
||||
try {
|
||||
if(F.exists()) {
|
||||
std::ostringstream OS;
|
||||
std::ifstream IF(FileName);
|
||||
Poco::StreamCopier::copyStream(IF, OS);
|
||||
Registry_ = nlohmann::json::parse(OS.str());
|
||||
}
|
||||
} catch (...) {
|
||||
Registry_ = nlohmann::json::parse("{}");
|
||||
}
|
||||
}
|
||||
|
||||
static AppServiceRegistry & instance() {
|
||||
static auto instance_= new AppServiceRegistry;
|
||||
return *instance_;
|
||||
}
|
||||
|
||||
inline ~AppServiceRegistry() {
|
||||
Save();
|
||||
}
|
||||
|
||||
inline void Save() {
|
||||
std::istringstream IS( to_string(Registry_));
|
||||
std::ofstream OF;
|
||||
OF.open(FileName,std::ios::binary | std::ios::trunc);
|
||||
Poco::StreamCopier::copyStream(IS, OF);
|
||||
}
|
||||
|
||||
inline void Set(const char *Key, uint64_t Value ) {
|
||||
Registry_[Key] = Value;
|
||||
Save();
|
||||
}
|
||||
|
||||
inline void Set(const char *Key, const std::string &Value ) {
|
||||
Registry_[Key] = Value;
|
||||
Save();
|
||||
}
|
||||
|
||||
inline void Set(const char *Key, bool Value ) {
|
||||
Registry_[Key] = Value;
|
||||
Save();
|
||||
}
|
||||
|
||||
inline bool Get(const char *Key, bool & Value ) {
|
||||
if(Registry_[Key].is_boolean()) {
|
||||
Value = Registry_[Key].get<bool>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Get(const char *Key, uint64_t & Value ) {
|
||||
if(Registry_[Key].is_number_unsigned()) {
|
||||
Value = Registry_[Key].get<uint64_t>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Get(const char *Key, std::string & Value ) {
|
||||
if(Registry_[Key].is_string()) {
|
||||
Value = Registry_[Key].get<std::string>();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string FileName;
|
||||
nlohmann::json Registry_;
|
||||
};
|
||||
|
||||
inline auto AppServiceRegistry() { return AppServiceRegistry::instance(); }
|
||||
|
||||
}
|
||||
129
src/framework/AuthClient.cpp
Normal file
129
src/framework/AuthClient.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#include "Poco/Net/HTTPServerResponse.h"
|
||||
|
||||
#include "framework/AuthClient.h"
|
||||
#include "framework/MicroServiceNames.h"
|
||||
#include "framework/OpenAPIRequests.h"
|
||||
#include "framework/utils.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
bool AuthClient::RetrieveTokenInformation(const std::string & SessionToken,
|
||||
SecurityObjects::UserInfoAndPolicy & UInfo,
|
||||
std::uint64_t TID,
|
||||
bool & Expired, bool & Contacted, bool Sub) {
|
||||
try {
|
||||
Types::StringPairVec QueryData;
|
||||
QueryData.push_back(std::make_pair("token",SessionToken));
|
||||
std::string AlternateURIForLogging = fmt::format("{}?token={}", Sub ? "/api/v1/validateSubToken" : "/api/v1/validateToken", Utils::SanitizeToken(SessionToken));
|
||||
OpenAPIRequestGet Req( uSERVICE_SECURITY,
|
||||
Sub ? "/api/v1/validateSubToken" : "/api/v1/validateToken",
|
||||
QueryData,
|
||||
10000,
|
||||
AlternateURIForLogging
|
||||
);
|
||||
Poco::JSON::Object::Ptr Response;
|
||||
|
||||
auto StatusCode = Req.Do(Response);
|
||||
if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT) {
|
||||
Contacted = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Contacted = true;
|
||||
if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_OK) {
|
||||
if(Response->has("tokenInfo") && Response->has("userInfo")) {
|
||||
UInfo.from_json(Response);
|
||||
if(IsTokenExpired(UInfo.webtoken)) {
|
||||
Expired = true;
|
||||
return false;
|
||||
}
|
||||
Expired = false;
|
||||
Cache_.update(SessionToken, UInfo);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
poco_error(Logger(),fmt::format("Failed to retrieve token={} for TID={}", Utils::SanitizeToken(SessionToken), TID));
|
||||
}
|
||||
Expired = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AuthClient::IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo,
|
||||
std::uint64_t TID,
|
||||
bool & Expired, bool & Contacted, bool Sub) {
|
||||
auto User = Cache_.get(SessionToken);
|
||||
if(!User.isNull()) {
|
||||
if(IsTokenExpired(User->webtoken)) {
|
||||
Expired = true;
|
||||
Cache_.remove(SessionToken);
|
||||
return false;
|
||||
}
|
||||
Expired = false;
|
||||
UInfo = *User;
|
||||
return true;
|
||||
}
|
||||
return RetrieveTokenInformation(SessionToken, UInfo, TID, Expired, Contacted, Sub);
|
||||
}
|
||||
|
||||
bool AuthClient::RetrieveApiKeyInformation(const std::string & SessionToken,
|
||||
SecurityObjects::UserInfoAndPolicy & UInfo,
|
||||
std::uint64_t TID,
|
||||
bool & Expired, bool & Contacted) {
|
||||
try {
|
||||
Types::StringPairVec QueryData;
|
||||
QueryData.push_back(std::make_pair("apikey",SessionToken));
|
||||
std::string AlternateURIForLogging = fmt::format("/api/v1/validateApiKey?apiKey={}", Utils::SanitizeToken(SessionToken));
|
||||
OpenAPIRequestGet Req( uSERVICE_SECURITY,
|
||||
"/api/v1/validateApiKey" ,
|
||||
QueryData,
|
||||
10000,
|
||||
AlternateURIForLogging);
|
||||
Poco::JSON::Object::Ptr Response;
|
||||
|
||||
auto StatusCode = Req.Do(Response);
|
||||
if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT) {
|
||||
Contacted = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
Contacted = true;
|
||||
if(StatusCode==Poco::Net::HTTPServerResponse::HTTP_OK) {
|
||||
if(Response->has("tokenInfo") && Response->has("userInfo") && Response->has("expiresOn")) {
|
||||
UInfo.from_json(Response);
|
||||
Expired = false;
|
||||
ApiKeyCache_.update(SessionToken, ApiKeyCacheEntry{ .UserInfo = UInfo, .ExpiresOn = Response->get("expiresOn")});
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
poco_error(Logger(),fmt::format("Failed to retrieve api key={} for TID={}", Utils::SanitizeToken(SessionToken), TID));
|
||||
}
|
||||
Expired = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AuthClient::IsValidApiKey(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy &UInfo,
|
||||
std::uint64_t TID, bool &Expired, bool &Contacted) {
|
||||
auto User = ApiKeyCache_.get(SessionToken);
|
||||
if (!User.isNull()) {
|
||||
if(User->ExpiresOn < Utils::Now()) {
|
||||
Expired = false;
|
||||
UInfo = User->UserInfo;
|
||||
return true;
|
||||
}
|
||||
ApiKeyCache_.remove(SessionToken);
|
||||
}
|
||||
return RetrieveApiKeyInformation(SessionToken, UInfo, TID, Expired, Contacted);
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
79
src/framework/AuthClient.h
Normal file
79
src/framework/AuthClient.h
Normal file
@@ -0,0 +1,79 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
|
||||
#include "Poco/ExpireLRUCache.h"
|
||||
#include "framework/utils.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class AuthClient : public SubSystemServer {
|
||||
|
||||
public:
|
||||
explicit AuthClient() noexcept:
|
||||
SubSystemServer("Authentication", "AUTH-CLNT", "authentication")
|
||||
{
|
||||
}
|
||||
|
||||
static auto instance() {
|
||||
static auto instance_ = new AuthClient;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
struct ApiKeyCacheEntry {
|
||||
OpenWifi::SecurityObjects::UserInfoAndPolicy UserInfo;
|
||||
std::uint64_t ExpiresOn;
|
||||
};
|
||||
|
||||
inline int Start() override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void Stop() override {
|
||||
poco_information(Logger(),"Stopping...");
|
||||
std::lock_guard G(Mutex_);
|
||||
Cache_.clear();
|
||||
poco_information(Logger(),"Stopped...");
|
||||
}
|
||||
|
||||
inline void RemovedCachedToken(const std::string &Token) {
|
||||
Cache_.remove(Token);
|
||||
ApiKeyCache_.remove(Token);
|
||||
}
|
||||
|
||||
inline static bool IsTokenExpired(const SecurityObjects::WebToken &T) {
|
||||
return ((T.expires_in_+T.created_) < Utils::Now());
|
||||
}
|
||||
|
||||
bool RetrieveTokenInformation(const std::string & SessionToken,
|
||||
SecurityObjects::UserInfoAndPolicy & UInfo,
|
||||
std::uint64_t TID,
|
||||
bool & Expired, bool & Contacted, bool Sub=false);
|
||||
|
||||
bool RetrieveApiKeyInformation(const std::string & SessionToken,
|
||||
SecurityObjects::UserInfoAndPolicy & UInfo,
|
||||
std::uint64_t TID,
|
||||
bool & Expired, bool & Contacted);
|
||||
|
||||
bool IsAuthorized(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo,
|
||||
std::uint64_t TID,
|
||||
bool & Expired, bool & Contacted, bool Sub = false);
|
||||
|
||||
bool IsValidApiKey(const std::string &SessionToken, SecurityObjects::UserInfoAndPolicy & UInfo,
|
||||
std::uint64_t TID,
|
||||
bool & Expired, bool & Contacted);
|
||||
|
||||
private:
|
||||
|
||||
Poco::ExpireLRUCache<std::string,OpenWifi::SecurityObjects::UserInfoAndPolicy> Cache_{512,1200000 };
|
||||
Poco::ExpireLRUCache<std::string,ApiKeyCacheEntry> ApiKeyCache_{512,1200000 };
|
||||
};
|
||||
|
||||
inline auto AuthClient() { return AuthClient::instance(); }
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
155
src/framework/CIDR.h
Normal file
155
src/framework/CIDR.h
Normal file
@@ -0,0 +1,155 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Poco/Net/IPAddress.h"
|
||||
#include "Poco/StringTokenizer.h"
|
||||
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
|
||||
namespace OpenWifi::CIDR {
|
||||
|
||||
static bool cidr_match(const in_addr &addr, const in_addr &net, uint8_t bits) {
|
||||
if (bits == 0) {
|
||||
return true;
|
||||
}
|
||||
return !((addr.s_addr ^ net.s_addr) & htonl(0xFFFFFFFFu << (32 - bits)));
|
||||
}
|
||||
|
||||
static bool cidr6_match(const in6_addr &address, const in6_addr &network, uint8_t bits) {
|
||||
#ifdef __linux__
|
||||
const uint32_t *a = address.s6_addr32;
|
||||
const uint32_t *n = network.s6_addr32;
|
||||
#else
|
||||
const uint32_t *a = address.__u6_addr.__u6_addr32;
|
||||
const uint32_t *n = network.__u6_addr.__u6_addr32;
|
||||
#endif
|
||||
int bits_whole, bits_incomplete;
|
||||
bits_whole = bits >> 5; // number of whole u32
|
||||
bits_incomplete = bits & 0x1F; // number of bits in incomplete u32
|
||||
if (bits_whole) {
|
||||
if (memcmp(a, n, bits_whole << 2) != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (bits_incomplete) {
|
||||
uint32_t mask = htonl((0xFFFFFFFFu) << (32 - bits_incomplete));
|
||||
if ((a[bits_whole] ^ n[bits_whole]) & mask) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ConvertStringToLong(const char *S, unsigned long &L) {
|
||||
char *end;
|
||||
L = std::strtol(S, &end, 10);
|
||||
return end != S;
|
||||
}
|
||||
|
||||
static bool CidrIPinRange(const Poco::Net::IPAddress &IP, const std::string &Range) {
|
||||
Poco::StringTokenizer TimeTokens(Range, "/", Poco::StringTokenizer::TOK_TRIM);
|
||||
|
||||
Poco::Net::IPAddress RangeIP;
|
||||
if (Poco::Net::IPAddress::tryParse(TimeTokens[0], RangeIP)) {
|
||||
if (TimeTokens.count() == 2) {
|
||||
if (RangeIP.family() == Poco::Net::IPAddress::IPv4) {
|
||||
unsigned long MaskLength;
|
||||
if (ConvertStringToLong(TimeTokens[1].c_str(), MaskLength)) {
|
||||
return cidr_match(*static_cast<const in_addr *>(RangeIP.addr()),
|
||||
*static_cast<const in_addr *>(IP.addr()), MaskLength);
|
||||
}
|
||||
} else if (RangeIP.family() == Poco::Net::IPAddress::IPv6) {
|
||||
unsigned long MaskLength;
|
||||
if (ConvertStringToLong(TimeTokens[1].c_str(), MaskLength)) {
|
||||
return cidr6_match(*static_cast<const in6_addr *>(RangeIP.addr()),
|
||||
*static_cast<const in6_addr *>(IP.addr()), MaskLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// Ranges can be a single IP, of IP1-IP2, of A set of IPs: IP1,IP2,IP3, or a cidr IP/24
|
||||
// These can work for IPv6 too...
|
||||
//
|
||||
static bool ValidateRange(const std::string &R) {
|
||||
|
||||
auto Tokens = Poco::StringTokenizer(R, "-");
|
||||
if (Tokens.count() == 2) {
|
||||
Poco::Net::IPAddress a, b;
|
||||
if (!Poco::Net::IPAddress::tryParse(Tokens[0], a) &&
|
||||
Poco::Net::IPAddress::tryParse(Tokens[1], b))
|
||||
return false;
|
||||
return a.family() == b.family();
|
||||
}
|
||||
|
||||
Tokens = Poco::StringTokenizer(R, ",");
|
||||
if (Tokens.count() > 1) {
|
||||
return std::all_of(Tokens.begin(), Tokens.end(), [](const std::string &A) {
|
||||
Poco::Net::IPAddress a;
|
||||
return Poco::Net::IPAddress::tryParse(A, a);
|
||||
});
|
||||
}
|
||||
|
||||
Tokens = Poco::StringTokenizer(R, "/");
|
||||
if (Tokens.count() == 2) {
|
||||
Poco::Net::IPAddress a;
|
||||
if (!Poco::Net::IPAddress::tryParse(Tokens[0], a))
|
||||
return false;
|
||||
if (std::atoi(Tokens[1].c_str()) == 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
Poco::Net::IPAddress a;
|
||||
return Poco::Net::IPAddress::tryParse(R, a);
|
||||
}
|
||||
|
||||
static bool IpInRange(const Poco::Net::IPAddress &target, const std::string &R) {
|
||||
|
||||
auto Tokens = Poco::StringTokenizer(R, "-");
|
||||
if (Tokens.count() == 2) {
|
||||
auto a = Poco::Net::IPAddress::parse(Tokens[0]);
|
||||
auto b = Poco::Net::IPAddress::parse(Tokens[1]);
|
||||
if (target.family() != a.family())
|
||||
return false;
|
||||
return (a <= target && b >= target);
|
||||
}
|
||||
|
||||
Tokens = Poco::StringTokenizer(R, ",");
|
||||
if (Tokens.count() > 1) {
|
||||
return std::any_of(Tokens.begin(), Tokens.end(), [target](const std::string &Element) {
|
||||
return Poco::Net::IPAddress::parse(Element) == target;
|
||||
});
|
||||
}
|
||||
|
||||
Tokens = Poco::StringTokenizer(R, "/");
|
||||
if (Tokens.count() == 2) {
|
||||
return CidrIPinRange(target, R);
|
||||
}
|
||||
|
||||
return Poco::Net::IPAddress::parse(R) == target;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool IpInRanges(const std::string &IP, const Types::StringVec &R) {
|
||||
Poco::Net::IPAddress Target;
|
||||
|
||||
if (!Poco::Net::IPAddress::tryParse(IP, Target))
|
||||
return false;
|
||||
|
||||
return std::any_of(cbegin(R), cend(R),
|
||||
[Target](const std::string &i) { return IpInRange(Target, i); });
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool ValidateIpRanges(const Types::StringVec &Ranges) {
|
||||
return std::all_of(cbegin(Ranges), cend(Ranges), ValidateRange);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/json-schema.hpp>
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/SubSystemServer.h"
|
||||
|
||||
using nlohmann::json;
|
||||
using nlohmann::json_schema::json_validator;
|
||||
@@ -32,7 +32,7 @@ namespace OpenWifi {
|
||||
nlohmann::json RootSchema_;
|
||||
|
||||
ConfigurationValidator():
|
||||
SubSystemServer("configvalidator", "CFG-VALIDATOR", "config.validator") {
|
||||
SubSystemServer("ConfigValidator", "CFG-VALIDATOR", "config.validator") {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
49
src/framework/EventBusManager.cpp
Normal file
49
src/framework/EventBusManager.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-26.
|
||||
//
|
||||
|
||||
#include "framework/EventBusManager.h"
|
||||
#include "framework/KafkaManager.h"
|
||||
#include "framework/utils.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
EventBusManager::EventBusManager(Poco::Logger &L) :
|
||||
Logger_(L) {
|
||||
}
|
||||
|
||||
void EventBusManager::run() {
|
||||
Running_ = true;
|
||||
Utils::SetThreadName("fmwk:EventMgr");
|
||||
auto Msg = MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_JOIN);
|
||||
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroServicePrivateEndPoint(),Msg, false);
|
||||
while(Running_) {
|
||||
Poco::Thread::trySleep((unsigned long)MicroServiceDaemonBusTimer());
|
||||
if(!Running_)
|
||||
break;
|
||||
Msg = MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE);
|
||||
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroServicePrivateEndPoint(),Msg, false);
|
||||
}
|
||||
Msg = MicroServiceMakeSystemEventMessage(KafkaTopics::ServiceEvents::EVENT_LEAVE);
|
||||
KafkaManager()->PostMessage(KafkaTopics::SERVICE_EVENTS,MicroServicePrivateEndPoint(),Msg, false);
|
||||
};
|
||||
|
||||
void EventBusManager::Start() {
|
||||
poco_information(Logger(),"Starting...");
|
||||
if(KafkaManager()->Enabled()) {
|
||||
Thread_.start(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void EventBusManager::Stop() {
|
||||
if(KafkaManager()->Enabled()) {
|
||||
poco_information(Logger(),"Stopping...");
|
||||
Running_ = false;
|
||||
Thread_.wakeUp();
|
||||
Thread_.join();
|
||||
poco_information(Logger(),"Stopped...");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
28
src/framework/EventBusManager.h
Normal file
28
src/framework/EventBusManager.h
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-26.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Poco/Runnable.h"
|
||||
#include "Poco/Logger.h"
|
||||
#include "Poco/Thread.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class EventBusManager : public Poco::Runnable {
|
||||
public:
|
||||
explicit EventBusManager(Poco::Logger &L);
|
||||
void run() final;
|
||||
void Start();
|
||||
void Stop();
|
||||
inline Poco::Logger & Logger() { return Logger_; }
|
||||
|
||||
private:
|
||||
mutable std::atomic_bool Running_ = false;
|
||||
Poco::Thread Thread_;
|
||||
Poco::Logger &Logger_;
|
||||
};
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
365
src/framework/KafkaManager.cpp
Normal file
365
src/framework/KafkaManager.cpp
Normal file
@@ -0,0 +1,365 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#include "KafkaManager.h"
|
||||
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
void KafkaLoggerFun([[maybe_unused]] cppkafka::KafkaHandleBase & handle, int level, const std::string & facility, const std::string &message) {
|
||||
switch ((cppkafka::LogLevel) level) {
|
||||
case cppkafka::LogLevel::LogNotice: {
|
||||
poco_notice(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message));
|
||||
}
|
||||
break;
|
||||
case cppkafka::LogLevel::LogDebug: {
|
||||
poco_debug(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message));
|
||||
}
|
||||
break;
|
||||
case cppkafka::LogLevel::LogInfo: {
|
||||
poco_information(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message));
|
||||
}
|
||||
break;
|
||||
case cppkafka::LogLevel::LogWarning: {
|
||||
poco_warning(KafkaManager()->Logger(), fmt::format("kafka-log: facility: {} message: {}",facility, message));
|
||||
}
|
||||
break;
|
||||
case cppkafka::LogLevel::LogAlert:
|
||||
case cppkafka::LogLevel::LogCrit: {
|
||||
poco_critical(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message));
|
||||
}
|
||||
break;
|
||||
case cppkafka::LogLevel::LogErr:
|
||||
case cppkafka::LogLevel::LogEmerg:
|
||||
default: {
|
||||
poco_error(KafkaManager()->Logger(),fmt::format("kafka-log: facility: {} message: {}",facility, message));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline void KafkaErrorFun([[maybe_unused]] cppkafka::KafkaHandleBase & handle, int error, const std::string &reason) {
|
||||
poco_error(KafkaManager()->Logger(),fmt::format("kafka-error: {}, reason: {}", error, reason));
|
||||
}
|
||||
|
||||
inline void AddKafkaSecurity(cppkafka::Configuration & Config) {
|
||||
auto CA = MicroServiceConfigGetString("openwifi.kafka.ssl.ca.location","");
|
||||
auto Certificate = MicroServiceConfigGetString("openwifi.kafka.ssl.certificate.location","");
|
||||
auto Key = MicroServiceConfigGetString("openwifi.kafka.ssl.key.location","");
|
||||
auto Password = MicroServiceConfigGetString("openwifi.kafka.ssl.key.password","");
|
||||
|
||||
if(CA.empty() || Certificate.empty() || Key.empty())
|
||||
return;
|
||||
|
||||
Config.set("ssl.ca.location", CA);
|
||||
Config.set("ssl.certificate.location", Certificate);
|
||||
Config.set("ssl.key.location", Key);
|
||||
if(!Password.empty())
|
||||
Config.set("ssl.key.password", Password);
|
||||
}
|
||||
|
||||
|
||||
void KafkaManager::initialize(Poco::Util::Application & self) {
|
||||
SubSystemServer::initialize(self);
|
||||
KafkaEnabled_ = MicroServiceConfigGetBool("openwifi.kafka.enable",false);
|
||||
}
|
||||
|
||||
inline void KafkaProducer::run() {
|
||||
Poco::Logger &Logger_ = Poco::Logger::create("KAFKA-PRODUCER", KafkaManager()->Logger().getChannel());
|
||||
poco_information(Logger_,"Starting...");
|
||||
|
||||
Utils::SetThreadName("Kafka:Prod");
|
||||
cppkafka::Configuration Config({
|
||||
{ "client.id", MicroServiceConfigGetString("openwifi.kafka.client.id", "") },
|
||||
{ "metadata.broker.list", MicroServiceConfigGetString("openwifi.kafka.brokerlist", "") }
|
||||
});
|
||||
|
||||
AddKafkaSecurity(Config);
|
||||
|
||||
Config.set_log_callback(KafkaLoggerFun);
|
||||
Config.set_error_callback(KafkaErrorFun);
|
||||
|
||||
KafkaManager()->SystemInfoWrapper_ = R"lit({ "system" : { "id" : )lit" +
|
||||
std::to_string(MicroServiceID()) +
|
||||
R"lit( , "host" : ")lit" + MicroServicePrivateEndPoint() +
|
||||
R"lit(" } , "payload" : )lit" ;
|
||||
|
||||
cppkafka::Producer Producer(Config);
|
||||
Running_ = true;
|
||||
|
||||
Poco::AutoPtr<Poco::Notification> Note(Queue_.waitDequeueNotification());
|
||||
while(Note && Running_) {
|
||||
try {
|
||||
auto Msg = dynamic_cast<KafkaMessage *>(Note.get());
|
||||
if (Msg != nullptr) {
|
||||
Producer.produce(
|
||||
cppkafka::MessageBuilder(Msg->Topic()).key(Msg->Key()).payload(Msg->Payload()));
|
||||
}
|
||||
} catch (const cppkafka::HandleException &E) {
|
||||
poco_warning(Logger_,fmt::format("Caught a Kafka exception (producer): {}", E.what()));
|
||||
} catch( const Poco::Exception &E) {
|
||||
Logger_.log(E);
|
||||
} catch (...) {
|
||||
poco_error(Logger_,"std::exception");
|
||||
}
|
||||
Note = Queue_.waitDequeueNotification();
|
||||
}
|
||||
poco_information(Logger_,"Stopped...");
|
||||
}
|
||||
|
||||
inline void KafkaConsumer::run() {
|
||||
Utils::SetThreadName("Kafka:Cons");
|
||||
|
||||
Poco::Logger &Logger_ = Poco::Logger::create("KAFKA-CONSUMER", KafkaManager()->Logger().getChannel());
|
||||
|
||||
poco_information(Logger_,"Starting...");
|
||||
|
||||
cppkafka::Configuration Config({
|
||||
{ "client.id", MicroServiceConfigGetString("openwifi.kafka.client.id","") },
|
||||
{ "metadata.broker.list", MicroServiceConfigGetString("openwifi.kafka.brokerlist","") },
|
||||
{ "group.id", MicroServiceConfigGetString("openwifi.kafka.group.id","") },
|
||||
{ "enable.auto.commit", MicroServiceConfigGetBool("openwifi.kafka.auto.commit",false) },
|
||||
{ "auto.offset.reset", "latest" } ,
|
||||
{ "enable.partition.eof", false }
|
||||
});
|
||||
|
||||
AddKafkaSecurity(Config);
|
||||
|
||||
Config.set_log_callback(KafkaLoggerFun);
|
||||
Config.set_error_callback(KafkaErrorFun);
|
||||
|
||||
cppkafka::TopicConfiguration topic_config = {
|
||||
{ "auto.offset.reset", "smallest" }
|
||||
};
|
||||
|
||||
// Now configure it to be the default topic config
|
||||
Config.set_default_topic_configuration(topic_config);
|
||||
|
||||
cppkafka::Consumer Consumer(Config);
|
||||
Consumer.set_assignment_callback([&](cppkafka::TopicPartitionList& partitions) {
|
||||
if(!partitions.empty()) {
|
||||
poco_information(Logger_,fmt::format("Partition assigned: {}...",
|
||||
partitions.front().get_partition()));
|
||||
}
|
||||
});
|
||||
Consumer.set_revocation_callback([&](const cppkafka::TopicPartitionList& partitions) {
|
||||
if(!partitions.empty()) {
|
||||
poco_information(Logger_,fmt::format("Partition revocation: {}...",
|
||||
partitions.front().get_partition()));
|
||||
}
|
||||
});
|
||||
|
||||
bool AutoCommit = MicroServiceConfigGetBool("openwifi.kafka.auto.commit",false);
|
||||
auto BatchSize = MicroServiceConfigGetInt("openwifi.kafka.consumer.batchsize",20);
|
||||
|
||||
Types::StringVec Topics;
|
||||
KafkaManager()->Topics(Topics);
|
||||
Consumer.subscribe(Topics);
|
||||
|
||||
Running_ = true;
|
||||
while(Running_) {
|
||||
try {
|
||||
std::vector<cppkafka::Message> MsgVec = Consumer.poll_batch(BatchSize, std::chrono::milliseconds(100));
|
||||
for(auto const &Msg:MsgVec) {
|
||||
if (!Msg)
|
||||
continue;
|
||||
if (Msg.get_error()) {
|
||||
if (!Msg.is_eof()) {
|
||||
poco_error(Logger_,fmt::format("Error: {}", Msg.get_error().to_string()));
|
||||
}
|
||||
if(!AutoCommit)
|
||||
Consumer.async_commit(Msg);
|
||||
continue;
|
||||
}
|
||||
KafkaManager()->Dispatch(Msg.get_topic(), Msg.get_key(),Msg.get_payload() );
|
||||
if (!AutoCommit)
|
||||
Consumer.async_commit(Msg);
|
||||
}
|
||||
} catch (const cppkafka::HandleException &E) {
|
||||
poco_warning(Logger_,fmt::format("Caught a Kafka exception (consumer): {}", E.what()));
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger_.log(E);
|
||||
} catch (...) {
|
||||
poco_error(Logger_,"std::exception");
|
||||
}
|
||||
}
|
||||
Consumer.unsubscribe();
|
||||
poco_information(Logger_,"Stopped...");
|
||||
}
|
||||
|
||||
void KafkaProducer::Start() {
|
||||
if(!Running_) {
|
||||
Running_=true;
|
||||
Worker_.start(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaProducer::Stop() {
|
||||
if(Running_) {
|
||||
Running_=false;
|
||||
Queue_.wakeUpAll();
|
||||
Worker_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaProducer::Produce(const std::string &Topic, const std::string &Key, const std::string &Payload) {
|
||||
std::lock_guard G(Mutex_);
|
||||
Queue_.enqueueNotification( new KafkaMessage(Topic,Key,Payload));
|
||||
}
|
||||
|
||||
void KafkaConsumer::Start() {
|
||||
if(!Running_) {
|
||||
Running_=true;
|
||||
Worker_.start(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaConsumer::Stop() {
|
||||
if(Running_) {
|
||||
Running_=false;
|
||||
Worker_.wakeUp();
|
||||
Worker_.join();
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaDispatcher::Start() {
|
||||
if(!Running_) {
|
||||
Running_=true;
|
||||
Worker_.start(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaDispatcher::Stop() {
|
||||
if(Running_) {
|
||||
Running_=false;
|
||||
Queue_.wakeUpAll();
|
||||
Worker_.join();
|
||||
}
|
||||
}
|
||||
|
||||
auto KafkaDispatcher::RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) {
|
||||
std::lock_guard G(Mutex_);
|
||||
auto It = Notifiers_.find(Topic);
|
||||
if(It == Notifiers_.end()) {
|
||||
Types::TopicNotifyFunctionList L;
|
||||
L.emplace(L.end(),std::make_pair(F,FunctionId_));
|
||||
Notifiers_[Topic] = std::move(L);
|
||||
} else {
|
||||
It->second.emplace(It->second.end(),std::make_pair(F,FunctionId_));
|
||||
}
|
||||
return FunctionId_++;
|
||||
}
|
||||
|
||||
void KafkaDispatcher::UnregisterTopicWatcher(const std::string &Topic, int Id) {
|
||||
std::lock_guard G(Mutex_);
|
||||
auto It = Notifiers_.find(Topic);
|
||||
if(It != Notifiers_.end()) {
|
||||
Types::TopicNotifyFunctionList & L = It->second;
|
||||
for(auto it=L.begin(); it!=L.end(); it++)
|
||||
if(it->second == Id) {
|
||||
L.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaDispatcher::Dispatch(const std::string &Topic, const std::string &Key, const std::string &Payload) {
|
||||
std::lock_guard G(Mutex_);
|
||||
auto It = Notifiers_.find(Topic);
|
||||
if(It!=Notifiers_.end()) {
|
||||
Queue_.enqueueNotification(new KafkaMessage(Topic, Key, Payload));
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaDispatcher::run() {
|
||||
Poco::Logger &Logger_ = Poco::Logger::create("KAFKA-DISPATCHER", KafkaManager()->Logger().getChannel());
|
||||
poco_information(Logger_,"Starting...");
|
||||
Poco::AutoPtr<Poco::Notification> Note(Queue_.waitDequeueNotification());
|
||||
Utils::SetThreadName("kafka:dispatch");
|
||||
while(Note && Running_) {
|
||||
auto Msg = dynamic_cast<KafkaMessage*>(Note.get());
|
||||
if(Msg!= nullptr) {
|
||||
auto It = Notifiers_.find(Msg->Topic());
|
||||
if (It != Notifiers_.end()) {
|
||||
const auto & FL = It->second;
|
||||
for(const auto &[CallbackFunc,_]:FL) {
|
||||
CallbackFunc(Msg->Key(), Msg->Payload());
|
||||
}
|
||||
}
|
||||
}
|
||||
Note = Queue_.waitDequeueNotification();
|
||||
}
|
||||
poco_information(Logger_,"Stopped...");
|
||||
}
|
||||
|
||||
void KafkaDispatcher::Topics(std::vector<std::string> &T) {
|
||||
T.clear();
|
||||
for(const auto &[TopicName,_]:Notifiers_)
|
||||
T.push_back(TopicName);
|
||||
}
|
||||
|
||||
|
||||
int KafkaManager::Start() {
|
||||
if(!KafkaEnabled_)
|
||||
return 0;
|
||||
ConsumerThr_.Start();
|
||||
ProducerThr_.Start();
|
||||
Dispatcher_.Start();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void KafkaManager::Stop() {
|
||||
if(KafkaEnabled_) {
|
||||
poco_information(Logger(),"Stopping...");
|
||||
Dispatcher_.Stop();
|
||||
ProducerThr_.Stop();
|
||||
ConsumerThr_.Stop();
|
||||
poco_information(Logger(),"Stopped...");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaManager::PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage ) {
|
||||
if(KafkaEnabled_) {
|
||||
ProducerThr_.Produce(topic,key,WrapMessage ? WrapSystemId(PayLoad) : PayLoad);
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaManager::Dispatch(const std::string &Topic, const std::string & Key, const std::string &Payload) {
|
||||
Dispatcher_.Dispatch(Topic, Key, Payload);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string KafkaManager::WrapSystemId(const std::string & PayLoad) {
|
||||
return SystemInfoWrapper_ + PayLoad + "}";
|
||||
}
|
||||
|
||||
uint64_t KafkaManager::RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F) {
|
||||
if(KafkaEnabled_) {
|
||||
return Dispatcher_.RegisterTopicWatcher(Topic,F);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaManager::UnregisterTopicWatcher(const std::string &Topic, uint64_t Id) {
|
||||
if(KafkaEnabled_) {
|
||||
Dispatcher_.UnregisterTopicWatcher(Topic, Id);
|
||||
}
|
||||
}
|
||||
|
||||
void KafkaManager::Topics(std::vector<std::string> &T) {
|
||||
Dispatcher_.Topics(T);
|
||||
}
|
||||
|
||||
void KafkaManager::PartitionAssignment(const cppkafka::TopicPartitionList& partitions) {
|
||||
poco_information(Logger(),fmt::format("Partition assigned: {}...", partitions.front().get_partition()));
|
||||
}
|
||||
|
||||
void KafkaManager::PartitionRevocation(const cppkafka::TopicPartitionList& partitions) {
|
||||
poco_information(Logger(),fmt::format("Partition revocation: {}...",partitions.front().get_partition()));
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
122
src/framework/KafkaManager.h
Normal file
122
src/framework/KafkaManager.h
Normal file
@@ -0,0 +1,122 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Poco/Notification.h"
|
||||
#include "Poco/NotificationQueue.h"
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
#include "framework/utils.h"
|
||||
#include "framework/KafkaTopics.h"
|
||||
|
||||
#include "cppkafka/cppkafka.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class KafkaMessage: public Poco::Notification {
|
||||
public:
|
||||
KafkaMessage( const std::string &Topic, const std::string &Key, const std::string & Payload) :
|
||||
Topic_(Topic), Key_(Key), Payload_(Payload) {
|
||||
}
|
||||
|
||||
inline const std::string & Topic() { return Topic_; }
|
||||
inline const std::string & Key() { return Key_; }
|
||||
inline const std::string & Payload() { return Payload_; }
|
||||
|
||||
private:
|
||||
std::string Topic_;
|
||||
std::string Key_;
|
||||
std::string Payload_;
|
||||
};
|
||||
|
||||
class KafkaProducer : public Poco::Runnable {
|
||||
public:
|
||||
void run () override;
|
||||
void Start();
|
||||
void Stop();
|
||||
void Produce(const std::string &Topic, const std::string &Key, const std::string &Payload);
|
||||
|
||||
private:
|
||||
std::recursive_mutex Mutex_;
|
||||
Poco::Thread Worker_;
|
||||
mutable std::atomic_bool Running_=false;
|
||||
Poco::NotificationQueue Queue_;
|
||||
};
|
||||
|
||||
class KafkaConsumer : public Poco::Runnable {
|
||||
public:
|
||||
void run() override;
|
||||
void Start();
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
std::recursive_mutex Mutex_;
|
||||
Poco::Thread Worker_;
|
||||
mutable std::atomic_bool Running_=false;
|
||||
};
|
||||
|
||||
class KafkaDispatcher : public Poco::Runnable {
|
||||
public:
|
||||
void Start();
|
||||
void Stop();
|
||||
auto RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F);
|
||||
void UnregisterTopicWatcher(const std::string &Topic, int Id);
|
||||
void Dispatch(const std::string &Topic, const std::string &Key, const std::string &Payload);
|
||||
void run() override;
|
||||
void Topics(std::vector<std::string> &T);
|
||||
|
||||
private:
|
||||
std::recursive_mutex Mutex_;
|
||||
Types::NotifyTable Notifiers_;
|
||||
Poco::Thread Worker_;
|
||||
mutable std::atomic_bool Running_=false;
|
||||
uint64_t FunctionId_=1;
|
||||
Poco::NotificationQueue Queue_;
|
||||
};
|
||||
|
||||
class KafkaManager : public SubSystemServer {
|
||||
public:
|
||||
|
||||
friend class KafkaConsumer;
|
||||
friend class KafkaProducer;
|
||||
|
||||
inline void initialize(Poco::Util::Application & self) override;
|
||||
|
||||
static auto instance() {
|
||||
static auto instance_ = new KafkaManager;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
int Start() override;
|
||||
void Stop() override;
|
||||
|
||||
void PostMessage(const std::string &topic, const std::string & key, const std::string &PayLoad, bool WrapMessage = true );
|
||||
void Dispatch(const std::string &Topic, const std::string & Key, const std::string &Payload);
|
||||
[[nodiscard]] std::string WrapSystemId(const std::string & PayLoad);
|
||||
[[nodiscard]] inline bool Enabled() const { return KafkaEnabled_; }
|
||||
uint64_t RegisterTopicWatcher(const std::string &Topic, Types::TopicNotifyFunction &F);
|
||||
void UnregisterTopicWatcher(const std::string &Topic, uint64_t Id);
|
||||
void Topics(std::vector<std::string> &T);
|
||||
|
||||
private:
|
||||
bool KafkaEnabled_ = false;
|
||||
std::string SystemInfoWrapper_;
|
||||
KafkaProducer ProducerThr_;
|
||||
KafkaConsumer ConsumerThr_;
|
||||
KafkaDispatcher Dispatcher_;
|
||||
|
||||
void PartitionAssignment(const cppkafka::TopicPartitionList& partitions);
|
||||
void PartitionRevocation(const cppkafka::TopicPartitionList& partitions);
|
||||
|
||||
KafkaManager() noexcept:
|
||||
SubSystemServer("KafkaManager", "KAFKA-SVR", "openwifi.kafka") {
|
||||
}
|
||||
};
|
||||
|
||||
inline auto KafkaManager() { return KafkaManager::instance(); }
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
namespace OpenWifi::KafkaTopics {
|
||||
static const std::string HEALTHCHECK{"healthcheck"};
|
||||
static const std::string STATE{"state"};
|
||||
|
||||
712
src/framework/MicroService.cpp
Normal file
712
src/framework/MicroService.cpp
Normal file
@@ -0,0 +1,712 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-26.
|
||||
//
|
||||
|
||||
#include "Poco/FileChannel.h"
|
||||
#include "Poco/ConsoleChannel.h"
|
||||
#include "Poco/PatternFormatter.h"
|
||||
#include "Poco/FormattingChannel.h"
|
||||
#include "Poco/AsyncChannel.h"
|
||||
#include "Poco/NullChannel.h"
|
||||
#include "Poco/SplitterChannel.h"
|
||||
#include "Poco/Net/HTTPStreamFactory.h"
|
||||
#include "Poco/Net/HTTPSStreamFactory.h"
|
||||
#include "Poco/Net/FTPSStreamFactory.h"
|
||||
#include "Poco/Net/FTPStreamFactory.h"
|
||||
#include "Poco/Net/SSLManager.h"
|
||||
#include "Poco/JSON/JSONException.h"
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/MicroServiceErrorHandler.h"
|
||||
#include "framework/UI_WebSocketClientServer.h"
|
||||
#include "framework/MicroServiceNames.h"
|
||||
#include "framework/AuthClient.h"
|
||||
#include "framework/ALBserver.h"
|
||||
#include "framework/KafkaManager.h"
|
||||
#include "framework/RESTAPI_GenericServerAccounting.h"
|
||||
#include "framework/RESTAPI_ExtServer.h"
|
||||
#include "framework/RESTAPI_IntServer.h"
|
||||
#include "framework/utils.h"
|
||||
#include "framework/WebSocketLogger.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
void MicroService::Exit(int Reason) {
|
||||
std::exit(Reason);
|
||||
}
|
||||
|
||||
void MicroService::BusMessageReceived([[maybe_unused]] const std::string &Key, const std::string & Payload) {
|
||||
std::lock_guard G(InfraMutex_);
|
||||
try {
|
||||
Poco::JSON::Parser P;
|
||||
auto Object = P.parse(Payload).extract<Poco::JSON::Object::Ptr>();
|
||||
|
||||
if (Object->has(KafkaTopics::ServiceEvents::Fields::ID) &&
|
||||
Object->has(KafkaTopics::ServiceEvents::Fields::EVENT)) {
|
||||
uint64_t ID = Object->get(KafkaTopics::ServiceEvents::Fields::ID);
|
||||
auto Event = Object->get(KafkaTopics::ServiceEvents::Fields::EVENT).toString();
|
||||
if (ID != ID_) {
|
||||
if( Event==KafkaTopics::ServiceEvents::EVENT_JOIN ||
|
||||
Event==KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE ||
|
||||
Event==KafkaTopics::ServiceEvents::EVENT_LEAVE ) {
|
||||
if( Object->has(KafkaTopics::ServiceEvents::Fields::TYPE) &&
|
||||
Object->has(KafkaTopics::ServiceEvents::Fields::PUBLIC) &&
|
||||
Object->has(KafkaTopics::ServiceEvents::Fields::PRIVATE) &&
|
||||
Object->has(KafkaTopics::ServiceEvents::Fields::VRSN) &&
|
||||
Object->has(KafkaTopics::ServiceEvents::Fields::KEY)) {
|
||||
auto PrivateEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString();
|
||||
if (Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE && Services_.find(PrivateEndPoint) != Services_.end()) {
|
||||
Services_[PrivateEndPoint].LastUpdate = Utils::Now();
|
||||
} else if (Event == KafkaTopics::ServiceEvents::EVENT_LEAVE) {
|
||||
Services_.erase(PrivateEndPoint);
|
||||
poco_debug(logger(),fmt::format("Service {} ID={} leaving system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID));
|
||||
} else if (Event == KafkaTopics::ServiceEvents::EVENT_JOIN || Event == KafkaTopics::ServiceEvents::EVENT_KEEP_ALIVE) {
|
||||
poco_debug(logger(),fmt::format("Service {} ID={} joining system.",Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),ID));
|
||||
Services_[PrivateEndPoint] = Types::MicroServiceMeta{
|
||||
.Id = ID,
|
||||
.Type = Poco::toLower(Object->get(KafkaTopics::ServiceEvents::Fields::TYPE).toString()),
|
||||
.PrivateEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PRIVATE).toString(),
|
||||
.PublicEndPoint = Object->get(KafkaTopics::ServiceEvents::Fields::PUBLIC).toString(),
|
||||
.AccessKey = Object->get(KafkaTopics::ServiceEvents::Fields::KEY).toString(),
|
||||
.Version = Object->get(KafkaTopics::ServiceEvents::Fields::VRSN).toString(),
|
||||
.LastUpdate = Utils::Now() };
|
||||
|
||||
std::string SvcList;
|
||||
for (const auto &Svc: Services_) {
|
||||
if(SvcList.empty())
|
||||
SvcList = Svc.second.Type;
|
||||
else
|
||||
SvcList += ", " + Svc.second.Type;
|
||||
}
|
||||
poco_information(logger(),fmt::format("Current list of microservices: {}", SvcList));
|
||||
}
|
||||
} else {
|
||||
poco_error(logger(),fmt::format("KAFKA-MSG: invalid event '{}', missing a field.",Event));
|
||||
}
|
||||
} else if (Event==KafkaTopics::ServiceEvents::EVENT_REMOVE_TOKEN) {
|
||||
if(Object->has(KafkaTopics::ServiceEvents::Fields::TOKEN)) {
|
||||
#ifndef TIP_SECURITY_SERVICE
|
||||
AuthClient()->RemovedCachedToken(Object->get(KafkaTopics::ServiceEvents::Fields::TOKEN).toString());
|
||||
#endif
|
||||
} else {
|
||||
poco_error(logger(),fmt::format("KAFKA-MSG: invalid event '{}', missing token",Event));
|
||||
}
|
||||
} else {
|
||||
poco_error(logger(),fmt::format("Unknown Event: {} Source: {}", Event, ID));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
poco_error(logger(),"Bad bus message.");
|
||||
}
|
||||
|
||||
auto i=Services_.begin();
|
||||
auto now = Utils::Now();
|
||||
for(;i!=Services_.end();) {
|
||||
if((now - i->second.LastUpdate)>60) {
|
||||
i = Services_.erase(i);
|
||||
} else
|
||||
++i;
|
||||
}
|
||||
|
||||
} catch (const Poco::Exception &E) {
|
||||
logger().log(E);
|
||||
}
|
||||
}
|
||||
|
||||
Types::MicroServiceMetaVec MicroService::GetServices(const std::string & Type) {
|
||||
std::lock_guard G(InfraMutex_);
|
||||
|
||||
auto T = Poco::toLower(Type);
|
||||
Types::MicroServiceMetaVec Res;
|
||||
for(const auto &[_,ServiceRec]:Services_) {
|
||||
if(ServiceRec.Type==T)
|
||||
Res.push_back(ServiceRec);
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
Types::MicroServiceMetaVec MicroService::GetServices() {
|
||||
std::lock_guard G(InfraMutex_);
|
||||
|
||||
Types::MicroServiceMetaVec Res;
|
||||
for(const auto &[_,ServiceRec]:Services_) {
|
||||
Res.push_back(ServiceRec);
|
||||
}
|
||||
return Res;
|
||||
}
|
||||
|
||||
void MicroService::LoadConfigurationFile() {
|
||||
std::string Location = Poco::Environment::get(DAEMON_CONFIG_ENV_VAR,".");
|
||||
ConfigFileName_ = ConfigFileName_.empty() ? Location + "/" + DAEMON_PROPERTIES_FILENAME : ConfigFileName_;
|
||||
Poco::Path ConfigFile(ConfigFileName_);
|
||||
|
||||
if(!ConfigFile.isFile())
|
||||
{
|
||||
std::cerr << DAEMON_APP_NAME << ": Configuration "
|
||||
<< ConfigFile.toString() << " does not seem to exist. Please set " + DAEMON_CONFIG_ENV_VAR
|
||||
+ " env variable the path of the " + DAEMON_PROPERTIES_FILENAME + " file." << std::endl;
|
||||
std::exit(Poco::Util::Application::EXIT_CONFIG);
|
||||
}
|
||||
|
||||
// loadConfiguration(ConfigFile.toString());
|
||||
PropConfigurationFile_ = new Poco::Util::PropertyFileConfiguration(ConfigFile.toString());
|
||||
configPtr()->addWriteable(PropConfigurationFile_, PRIO_DEFAULT);
|
||||
}
|
||||
|
||||
void MicroService::Reload() {
|
||||
LoadConfigurationFile();
|
||||
LoadMyConfig();
|
||||
}
|
||||
|
||||
void MicroService::LoadMyConfig() {
|
||||
NoAPISecurity_ = ConfigGetBool("openwifi.security.restapi.disable",false);
|
||||
std::string KeyFile = ConfigPath("openwifi.service.key","");
|
||||
if(!KeyFile.empty()) {
|
||||
std::string KeyFilePassword = ConfigPath("openwifi.service.key.password", "");
|
||||
AppKey_ = Poco::SharedPtr<Poco::Crypto::RSAKey>(new Poco::Crypto::RSAKey("", KeyFile, KeyFilePassword));
|
||||
Cipher_ = CipherFactory_.createCipher(*AppKey_);
|
||||
Signer_.setRSAKey(AppKey_);
|
||||
Signer_.addAllAlgorithms();
|
||||
NoBuiltInCrypto_ = false;
|
||||
} else {
|
||||
NoBuiltInCrypto_ = true;
|
||||
}
|
||||
|
||||
ID_ = Utils::GetSystemId();
|
||||
if(!DebugMode_)
|
||||
DebugMode_ = ConfigGetBool("openwifi.system.debug",false);
|
||||
MyPrivateEndPoint_ = ConfigGetString("openwifi.system.uri.private");
|
||||
MyPublicEndPoint_ = ConfigGetString("openwifi.system.uri.public");
|
||||
UIURI_ = ConfigGetString("openwifi.system.uri.ui");
|
||||
MyHash_ = Utils::ComputeHash(MyPublicEndPoint_);
|
||||
}
|
||||
|
||||
void MicroService::InitializeLoggingSystem() {
|
||||
static auto initialized = false;
|
||||
|
||||
if(!initialized) {
|
||||
initialized = true;
|
||||
LoadConfigurationFile();
|
||||
|
||||
auto LoggingDestination = MicroService::instance().ConfigGetString("logging.type", "file");
|
||||
auto LoggingFormat = MicroService::instance().ConfigGetString("logging.format",
|
||||
"%Y-%m-%d %H:%M:%S.%i %s: [%p][thr:%I] %t");
|
||||
auto UseAsyncLogs_ = MicroService::instance().ConfigGetBool("logging.asynch", true);
|
||||
auto DisableWebSocketLogging = MicroService::instance().ConfigGetBool("logging.websocket",false);
|
||||
|
||||
if (LoggingDestination == "null") {
|
||||
Poco::AutoPtr<Poco::NullChannel> DevNull(new Poco::NullChannel);
|
||||
Poco::Logger::root().setChannel(DevNull);
|
||||
} else if (LoggingDestination == "console") {
|
||||
SetConsoleLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat);
|
||||
} else if (LoggingDestination == "colorconsole") {
|
||||
SetColorConsoleLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat);
|
||||
} else if (LoggingDestination == "sql") {
|
||||
SetSQLLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat);
|
||||
} else if (LoggingDestination == "syslog") {
|
||||
SetSyslogLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat);
|
||||
} else {
|
||||
SetFileLogs(UseAsyncLogs_, DisableWebSocketLogging, LoggingFormat, DAEMON_ROOT_ENV_VAR);
|
||||
}
|
||||
|
||||
auto Level = Poco::Logger::parseLevel(MicroService::instance().ConfigGetString("logging.level", "debug"));
|
||||
Poco::Logger::root().setLevel(Level);
|
||||
if(!DisableWebSocketLogging) {
|
||||
static const UI_WebSocketClientServer::NotificationTypeIdVec Notifications = {
|
||||
{1, "log"}};
|
||||
UI_WebSocketClientServer()->RegisterNotifications(Notifications);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MicroService::SetConsoleLogs(bool UseAsync, bool DisableWebSocketLogging, const std::string & FormatterPattern) {
|
||||
|
||||
Poco::AutoPtr<Poco::ConsoleChannel> Console(new Poco::ConsoleChannel);
|
||||
Poco::AutoPtr<Poco::PatternFormatter> Formatter(new Poco::PatternFormatter);
|
||||
Formatter->setProperty("pattern", FormatterPattern);
|
||||
Poco::AutoPtr<Poco::FormattingChannel> FormattingChannel(new Poco::FormattingChannel(Formatter, Console));
|
||||
|
||||
if(DisableWebSocketLogging) {
|
||||
if(UseAsync) {
|
||||
Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(FormattingChannel));
|
||||
Poco::Logger::root().setChannel(Async);
|
||||
} else {
|
||||
Poco::Logger::root().setChannel(FormattingChannel);
|
||||
}
|
||||
} else {
|
||||
Poco::AutoPtr<WebSocketLogger> WSLogger(new WebSocketLogger);
|
||||
Poco::AutoPtr<Poco::SplitterChannel> Splitter(new Poco::SplitterChannel);
|
||||
Splitter->addChannel(WSLogger);
|
||||
Splitter->addChannel(FormattingChannel);
|
||||
if(UseAsync) {
|
||||
Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(Splitter));
|
||||
Poco::Logger::root().setChannel(Async);
|
||||
} else {
|
||||
Poco::Logger::root().setChannel(Splitter);
|
||||
}
|
||||
}
|
||||
Poco::Logger::root().information(fmt::format("Enabled console logs: asynch={} websocket={}",UseAsync,DisableWebSocketLogging));
|
||||
}
|
||||
|
||||
void MicroService::SetColorConsoleLogs(bool UseAsync, bool DisableWebSocketLogging, const std::string & FormatterPattern) {
|
||||
|
||||
Poco::AutoPtr<Poco::ColorConsoleChannel> Console(new Poco::ColorConsoleChannel);
|
||||
Poco::AutoPtr<Poco::PatternFormatter> Formatter(new Poco::PatternFormatter);
|
||||
Formatter->setProperty("pattern", FormatterPattern);
|
||||
Poco::AutoPtr<Poco::FormattingChannel> FormattingChannel(new Poco::FormattingChannel(Formatter, Console));
|
||||
|
||||
if(DisableWebSocketLogging) {
|
||||
if(UseAsync) {
|
||||
Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(FormattingChannel));
|
||||
Poco::Logger::root().setChannel(Async);
|
||||
} else {
|
||||
Poco::Logger::root().setChannel(FormattingChannel);
|
||||
}
|
||||
} else {
|
||||
Poco::AutoPtr<WebSocketLogger> WSLogger(new WebSocketLogger);
|
||||
Poco::AutoPtr<Poco::SplitterChannel> Splitter(new Poco::SplitterChannel);
|
||||
Splitter->addChannel(WSLogger);
|
||||
Splitter->addChannel(FormattingChannel);
|
||||
if(UseAsync) {
|
||||
Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(Splitter));
|
||||
Poco::Logger::root().setChannel(Async);
|
||||
} else {
|
||||
Poco::Logger::root().setChannel(Splitter);
|
||||
}
|
||||
}
|
||||
Poco::Logger::root().information(fmt::format("Enabled color console logs: asynch={} websocket={}",UseAsync,DisableWebSocketLogging));
|
||||
}
|
||||
|
||||
void MicroService::SetSQLLogs([[maybe_unused]] bool UseAsync,[[maybe_unused]] bool DisableWebSocketLogging,[[maybe_unused]] const std::string & FormatterPattern) {
|
||||
//"CREATE TABLE T_POCO_LOG (Source VARCHAR, Name VARCHAR, ProcessId INTEGER, Thread VARCHAR, ThreadId INTEGER, Priority INTEGER, Text VARCHAR, DateTime DATE)"
|
||||
}
|
||||
|
||||
void MicroService::SetSyslogLogs([[maybe_unused]] bool UseAsync,[[maybe_unused]] bool DisableWebSocketLogging,[[maybe_unused]] const std::string & FormatterPattern) {
|
||||
|
||||
}
|
||||
|
||||
void MicroService::SetFileLogs(bool UseAsync, bool DisableWebSocketLogging, const std::string & FormatterPattern, const std::string & root_env_var) {
|
||||
std::string DefaultLogPath = fmt::format("${}/logs",root_env_var);
|
||||
auto LoggingLocationDir = MicroService::instance().ConfigPath("logging.path", DefaultLogPath);
|
||||
Poco::File LD(LoggingLocationDir);
|
||||
try {
|
||||
if(!LD.exists()) {
|
||||
LD.createDirectory();
|
||||
}
|
||||
} catch(const Poco::Exception &E) {
|
||||
std::cout << "Cannot create " << LD.path() << " Error: " << E.message() << std::endl;
|
||||
}
|
||||
auto LoggingLocationDirFilePattern = LoggingLocationDir + "/log";
|
||||
|
||||
Poco::AutoPtr<Poco::FileChannel> FileChannel(new Poco::FileChannel);
|
||||
FileChannel->setProperty("rotation", "10 M");
|
||||
FileChannel->setProperty("archive", "timestamp");
|
||||
FileChannel->setProperty("purgeCount", "10");
|
||||
FileChannel->setProperty("path", LoggingLocationDirFilePattern);
|
||||
|
||||
Poco::AutoPtr<Poco::PatternFormatter> Formatter(new Poco::PatternFormatter);
|
||||
Formatter->setProperty("pattern", FormatterPattern);
|
||||
Poco::AutoPtr<Poco::FormattingChannel> FormattingChannel(new Poco::FormattingChannel(Formatter, FileChannel));
|
||||
|
||||
if(DisableWebSocketLogging) {
|
||||
if(UseAsync) {
|
||||
Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(FormattingChannel));
|
||||
Poco::Logger::root().setChannel(Async);
|
||||
} else {
|
||||
Poco::Logger::root().setChannel(FormattingChannel);
|
||||
}
|
||||
} else {
|
||||
Poco::AutoPtr<WebSocketLogger> WSLogger(new WebSocketLogger);
|
||||
Poco::AutoPtr<Poco::SplitterChannel> Splitter(new Poco::SplitterChannel);
|
||||
Splitter->addChannel(WSLogger);
|
||||
Splitter->addChannel(FormattingChannel);
|
||||
if(UseAsync) {
|
||||
Poco::AutoPtr<Poco::AsyncChannel> Async(new Poco::AsyncChannel(Splitter));
|
||||
Poco::Logger::root().setChannel(Async);
|
||||
} else {
|
||||
Poco::Logger::root().setChannel(Splitter);
|
||||
}
|
||||
}
|
||||
Poco::Logger::root().information(fmt::format("Enabled file logs: asynch={} websocket={}",UseAsync,DisableWebSocketLogging));
|
||||
}
|
||||
|
||||
void DaemonPostInitialization(Poco::Util::Application &self);
|
||||
|
||||
void MicroService::initialize(Poco::Util::Application &self) {
|
||||
// add the default services
|
||||
LoadConfigurationFile();
|
||||
InitializeLoggingSystem();
|
||||
|
||||
SubSystems_.push_back(KafkaManager());
|
||||
SubSystems_.push_back(ALBHealthCheckServer());
|
||||
SubSystems_.push_back(RESTAPI_ExtServer());
|
||||
SubSystems_.push_back(RESTAPI_IntServer());
|
||||
#ifndef TIP_SECURITY_SERVICE
|
||||
SubSystems_.push_back(AuthClient());
|
||||
#endif
|
||||
Poco::Net::initializeSSL();
|
||||
Poco::Net::HTTPStreamFactory::registerFactory();
|
||||
Poco::Net::HTTPSStreamFactory::registerFactory();
|
||||
Poco::Net::FTPStreamFactory::registerFactory();
|
||||
Poco::Net::FTPSStreamFactory::registerFactory();
|
||||
|
||||
Poco::File DataDir(ConfigPath("openwifi.system.data"));
|
||||
DataDir_ = DataDir.path();
|
||||
if(!DataDir.exists()) {
|
||||
try {
|
||||
DataDir.createDirectory();
|
||||
} catch (const Poco::Exception &E) {
|
||||
logger().log(E);
|
||||
}
|
||||
}
|
||||
WWWAssetsDir_ = ConfigPath("openwifi.restapi.wwwassets","");
|
||||
if(WWWAssetsDir_.empty())
|
||||
WWWAssetsDir_ = DataDir_;
|
||||
|
||||
LoadMyConfig();
|
||||
|
||||
InitializeSubSystemServers();
|
||||
ServerApplication::initialize(self);
|
||||
DaemonPostInitialization(self);
|
||||
|
||||
Types::TopicNotifyFunction F = [this](const std::string &Key,const std::string &Payload) { this->BusMessageReceived(Key, Payload); };
|
||||
KafkaManager()->RegisterTopicWatcher(KafkaTopics::SERVICE_EVENTS, F);
|
||||
}
|
||||
|
||||
void MicroService::uninitialize() {
|
||||
// add your own uninitialization code here
|
||||
ServerApplication::uninitialize();
|
||||
}
|
||||
|
||||
void MicroService::reinitialize(Poco::Util::Application &self) {
|
||||
ServerApplication::reinitialize(self);
|
||||
// add your own reinitialization code here
|
||||
}
|
||||
|
||||
void MicroService::defineOptions(Poco::Util::OptionSet &options) {
|
||||
ServerApplication::defineOptions(options);
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("help", "", "display help information on command line arguments")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleHelp)));
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("file", "", "specify the configuration file")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.argument("file")
|
||||
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleConfig)));
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("debug", "", "to run in debug, set to true")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleDebug)));
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("logs", "", "specify the log directory and file (i.e. dir/file.log)")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.argument("dir")
|
||||
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleLogs)));
|
||||
|
||||
options.addOption(
|
||||
Poco::Util::Option("version", "", "get the version and quit.")
|
||||
.required(false)
|
||||
.repeatable(false)
|
||||
.callback(Poco::Util::OptionCallback<MicroService>(this, &MicroService::handleVersion)));
|
||||
|
||||
}
|
||||
|
||||
void MicroService::handleHelp([[maybe_unused]] const std::string &name, [[maybe_unused]] const std::string &value) {
|
||||
HelpRequested_ = true;
|
||||
displayHelp();
|
||||
stopOptionsProcessing();
|
||||
}
|
||||
|
||||
void MicroService::handleVersion([[maybe_unused]] const std::string &name, [[maybe_unused]] const std::string &value) {
|
||||
HelpRequested_ = true;
|
||||
std::cout << Version() << std::endl;
|
||||
stopOptionsProcessing();
|
||||
}
|
||||
|
||||
void MicroService::handleDebug([[maybe_unused]] const std::string &name, const std::string &value) {
|
||||
if(value == "true")
|
||||
DebugMode_ = true ;
|
||||
}
|
||||
|
||||
void MicroService::handleLogs([[maybe_unused]] const std::string &name, const std::string &value) {
|
||||
LogDir_ = value;
|
||||
}
|
||||
|
||||
void MicroService::handleConfig([[maybe_unused]] const std::string &name, const std::string &value) {
|
||||
ConfigFileName_ = value;
|
||||
}
|
||||
|
||||
void MicroService::displayHelp() {
|
||||
Poco::Util::HelpFormatter helpFormatter(options());
|
||||
helpFormatter.setCommand(commandName());
|
||||
helpFormatter.setUsage("OPTIONS");
|
||||
helpFormatter.setHeader("A " + DAEMON_APP_NAME + " implementation for TIP.");
|
||||
helpFormatter.format(std::cout);
|
||||
}
|
||||
|
||||
void MicroService::InitializeSubSystemServers() {
|
||||
for(auto i:SubSystems_) {
|
||||
addSubsystem(i);
|
||||
}
|
||||
}
|
||||
|
||||
void MicroService::StartSubSystemServers() {
|
||||
AddActivity("Starting");
|
||||
for(auto i:SubSystems_) {
|
||||
i->Start();
|
||||
}
|
||||
EventBusManager_ = std::make_unique<EventBusManager>(Poco::Logger::create("EventBusManager",Poco::Logger::root().getChannel(),Poco::Logger::root().getLevel()));
|
||||
EventBusManager_->Start();
|
||||
}
|
||||
|
||||
void MicroService::StopSubSystemServers() {
|
||||
AddActivity("Stopping");
|
||||
EventBusManager_->Stop();
|
||||
for(auto i=SubSystems_.rbegin(); i!=SubSystems_.rend(); ++i) {
|
||||
(*i)->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string MicroService::CreateUUID() {
|
||||
static std::random_device rd;
|
||||
static std::mt19937_64 gen(rd());
|
||||
static std::uniform_int_distribution<> dis(0, 15);
|
||||
static std::uniform_int_distribution<> dis2(8, 11);
|
||||
|
||||
std::stringstream ss;
|
||||
int i;
|
||||
ss << std::hex;
|
||||
for (i = 0; i < 8; i++) {
|
||||
ss << dis(gen);
|
||||
}
|
||||
ss << "-";
|
||||
for (i = 0; i < 4; i++) {
|
||||
ss << dis(gen);
|
||||
}
|
||||
ss << "-4";
|
||||
for (i = 0; i < 3; i++) {
|
||||
ss << dis(gen);
|
||||
}
|
||||
ss << "-";
|
||||
ss << dis2(gen);
|
||||
for (i = 0; i < 3; i++) {
|
||||
ss << dis(gen);
|
||||
}
|
||||
ss << "-";
|
||||
for (i = 0; i < 12; i++) {
|
||||
ss << dis(gen);
|
||||
};
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
bool MicroService::SetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) {
|
||||
try {
|
||||
auto P = Poco::Logger::parseLevel(Level);
|
||||
auto Sub = Poco::toLower(SubSystem);
|
||||
|
||||
if (Sub == "all") {
|
||||
for (auto i : SubSystems_) {
|
||||
i->Logger().setLevel(P);
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
for (auto i : SubSystems_) {
|
||||
if (Sub == Poco::toLower(i->Name())) {
|
||||
i->Logger().setLevel(P);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const Poco::Exception & E) {
|
||||
std::cerr << "Exception" << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MicroService::Reload(const std::string &Sub) {
|
||||
for (auto i : SubSystems_) {
|
||||
if (Poco::toLower(Sub) == Poco::toLower(i->Name())) {
|
||||
i->reinitialize(Poco::Util::Application::instance());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Types::StringVec MicroService::GetSubSystems() const {
|
||||
Types::StringVec Result;
|
||||
for(auto i:SubSystems_)
|
||||
Result.push_back(Poco::toLower(i->Name()));
|
||||
return Result;
|
||||
}
|
||||
|
||||
Types::StringPairVec MicroService::GetLogLevels() {
|
||||
Types::StringPairVec Result;
|
||||
|
||||
for(auto &i:SubSystems_) {
|
||||
auto P = std::make_pair( i->Name(), Utils::LogLevelToString(i->GetLoggingLevel()));
|
||||
Result.push_back(P);
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
const Types::StringVec & MicroService::GetLogLevelNames() {
|
||||
static Types::StringVec LevelNames{"none", "fatal", "critical", "error", "warning", "notice", "information", "debug", "trace" };
|
||||
return LevelNames;
|
||||
}
|
||||
|
||||
uint64_t MicroService::ConfigGetInt(const std::string &Key,uint64_t Default) {
|
||||
return (uint64_t) config().getInt64(Key,Default);
|
||||
}
|
||||
|
||||
uint64_t MicroService::ConfigGetInt(const std::string &Key) {
|
||||
return config().getInt(Key);
|
||||
}
|
||||
|
||||
uint64_t MicroService::ConfigGetBool(const std::string &Key,bool Default) {
|
||||
return config().getBool(Key,Default);
|
||||
}
|
||||
|
||||
uint64_t MicroService::ConfigGetBool(const std::string &Key) {
|
||||
return config().getBool(Key);
|
||||
}
|
||||
|
||||
std::string MicroService::ConfigGetString(const std::string &Key,const std::string & Default) {
|
||||
return config().getString(Key, Default);
|
||||
}
|
||||
|
||||
std::string MicroService::ConfigGetString(const std::string &Key) {
|
||||
return config().getString(Key);
|
||||
}
|
||||
|
||||
std::string MicroService::ConfigPath(const std::string &Key,const std::string & Default) {
|
||||
std::string R = config().getString(Key, Default);
|
||||
return Poco::Path::expand(R);
|
||||
}
|
||||
|
||||
std::string MicroService::ConfigPath(const std::string &Key) {
|
||||
std::string R = config().getString(Key);
|
||||
return Poco::Path::expand(R);
|
||||
}
|
||||
|
||||
std::string MicroService::Encrypt(const std::string &S) {
|
||||
if(NoBuiltInCrypto_) {
|
||||
return S;
|
||||
}
|
||||
return Cipher_->encryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);;
|
||||
}
|
||||
|
||||
std::string MicroService::Decrypt(const std::string &S) {
|
||||
if(NoBuiltInCrypto_) {
|
||||
return S;
|
||||
}
|
||||
return Cipher_->decryptString(S, Poco::Crypto::Cipher::Cipher::ENC_BASE64);;
|
||||
}
|
||||
|
||||
std::string MicroService::MakeSystemEventMessage( const std::string & Type ) const {
|
||||
Poco::JSON::Object Obj;
|
||||
Obj.set(KafkaTopics::ServiceEvents::Fields::EVENT,Type);
|
||||
Obj.set(KafkaTopics::ServiceEvents::Fields::ID,ID_);
|
||||
Obj.set(KafkaTopics::ServiceEvents::Fields::TYPE,Poco::toLower(DAEMON_APP_NAME));
|
||||
Obj.set(KafkaTopics::ServiceEvents::Fields::PUBLIC,MyPublicEndPoint_);
|
||||
Obj.set(KafkaTopics::ServiceEvents::Fields::PRIVATE,MyPrivateEndPoint_);
|
||||
Obj.set(KafkaTopics::ServiceEvents::Fields::KEY,MyHash_);
|
||||
Obj.set(KafkaTopics::ServiceEvents::Fields::VRSN,Version_);
|
||||
std::stringstream ResultText;
|
||||
Poco::JSON::Stringifier::stringify(Obj, ResultText);
|
||||
return ResultText.str();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool MicroService::IsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request) {
|
||||
try {
|
||||
auto APIKEY = Request.get("X-API-KEY");
|
||||
return APIKEY == MyHash_;
|
||||
} catch (const Poco::Exception &E) {
|
||||
logger().log(E);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MicroService::SavePID() {
|
||||
try {
|
||||
std::ofstream O;
|
||||
O.open(MicroService::instance().DataDir() + "/pidfile",std::ios::binary | std::ios::trunc);
|
||||
O << Poco::Process::id();
|
||||
O.close();
|
||||
} catch (...)
|
||||
{
|
||||
std::cout << "Could not save system ID" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int MicroService::main([[maybe_unused]] const ArgVec &args) {
|
||||
MicroServiceErrorHandler ErrorHandler(*this);
|
||||
Poco::ErrorHandler::set(&ErrorHandler);
|
||||
|
||||
if (!HelpRequested_) {
|
||||
SavePID();
|
||||
|
||||
Poco::Logger &logger = Poco::Logger::get(DAEMON_APP_NAME);
|
||||
logger.notice(fmt::format("Starting {} version {}.",DAEMON_APP_NAME, Version()));
|
||||
|
||||
if(Poco::Net::Socket::supportsIPv6())
|
||||
poco_information(logger,"System supports IPv6.");
|
||||
else
|
||||
poco_information(logger,"System does NOT support IPv6.");
|
||||
|
||||
if (config().getBool("application.runAsDaemon", false)) {
|
||||
poco_information(logger,"Starting as a daemon.");
|
||||
}
|
||||
|
||||
poco_information(logger,fmt::format("System ID set to {}",ID_));
|
||||
StartSubSystemServers();
|
||||
waitForTerminationRequest();
|
||||
StopSubSystemServers();
|
||||
logger.notice(fmt::format("Stopped {}...",DAEMON_APP_NAME));
|
||||
}
|
||||
|
||||
return Application::EXIT_OK;
|
||||
}
|
||||
|
||||
void MicroService::AddActivity(const std::string &Activity) {
|
||||
if(!DataDir_.empty()) {
|
||||
std::string ActivityFile{ DataDir_ + "/activity.log"};
|
||||
try {
|
||||
std::ofstream of(ActivityFile,std::ios_base::app | std::ios_base::out );
|
||||
auto t = std::chrono::system_clock::now();
|
||||
std::time_t now = std::chrono::system_clock::to_time_t(t);
|
||||
of << Activity << " at " << std::ctime(&now) ;
|
||||
} catch (...) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string MicroService::Sign(Poco::JWT::Token &T, const std::string &Algo) {
|
||||
if(NoBuiltInCrypto_) {
|
||||
return T.toString();
|
||||
} else {
|
||||
return Signer_.sign(T,Algo);
|
||||
}
|
||||
}
|
||||
|
||||
void MicroService::DeleteOverrideConfiguration() {
|
||||
Poco::File F(DataDir_ + ExtraConfigurationFilename);
|
||||
|
||||
try {
|
||||
if(F.exists())
|
||||
F.remove();
|
||||
} catch (...) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
212
src/framework/MicroServiceErrorHandler.h
Normal file
212
src/framework/MicroServiceErrorHandler.h
Normal file
@@ -0,0 +1,212 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-09-29.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "Poco/Util/Application.h"
|
||||
#include "Poco/ErrorHandler.h"
|
||||
#include "Poco/Net/NetException.h"
|
||||
#include "Poco/Net/SSLException.h"
|
||||
#include "Poco/JSON/Template.h"
|
||||
#include "Poco/JSON/JSONException.h"
|
||||
#include "Poco/Thread.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class MicroServiceErrorHandler : public Poco::ErrorHandler {
|
||||
public:
|
||||
explicit MicroServiceErrorHandler(Poco::Util::Application &App) : App_(App) {
|
||||
}
|
||||
|
||||
inline void exception(const Poco::Exception & Base) override {
|
||||
try {
|
||||
if(Poco::Thread::current()!= nullptr) {
|
||||
t_name = Poco::Thread::current()->getName();
|
||||
t_id = Poco::Thread::current()->id();
|
||||
} else {
|
||||
t_name = "startup_code";
|
||||
t_id = 0;
|
||||
}
|
||||
|
||||
App_.logger().log(Base);
|
||||
Base.rethrow();
|
||||
|
||||
} catch (const Poco::Net::InvalidCertificateException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::InvalidCertificateException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::InvalidSocketException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::InvalidSocketException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::WebSocketException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::WebSocketException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::ConnectionResetException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::ConnectionResetException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::CertificateValidationException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::CertificateValidationException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::SSLConnectionUnexpectedlyClosedException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::SSLConnectionUnexpectedlyClosedException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::SSLContextException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::SSLContextException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Net::SSLException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::SSLException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
|
||||
} catch (const Poco::Net::InvalidAddressException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::InvalidAddressException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
|
||||
} catch (const Poco::Net::NetException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Net::NetException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
|
||||
} catch (const Poco::IOException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::IOException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::TimeoutException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::TimeoutException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::NoThreadAvailableException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::NoThreadAvailableException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::OutOfMemoryException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::OutOfMemoryException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::BadCastException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::BadCastException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::DataException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::DataException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::PoolOverflowException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::PoolOverflowException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::SystemException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::SystemException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::RuntimeException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::RuntimeException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::JSON::JSONTemplateException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::JSON::JSONTemplateException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::JSON::JSONException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::JSON::JSONException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::ApplicationException &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::ApplicationException thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (const Poco::Exception &E) {
|
||||
poco_error(App_.logger(), fmt::format("Poco::Exception thr_name={} thr_id={} code={} text={} msg={} what={}",
|
||||
t_name, t_id, E.code(),
|
||||
E.displayText(),
|
||||
E.message(),
|
||||
E.what()));
|
||||
} catch (...) {
|
||||
poco_error(App_.logger(), fmt::format("Poco:Generic thr_name={}",t_name, t_id));
|
||||
}
|
||||
}
|
||||
|
||||
inline void exception(const std::exception & E) override {
|
||||
if(Poco::Thread::current()!= nullptr) {
|
||||
t_name = Poco::Thread::current()->getName();
|
||||
t_id = Poco::Thread::current()->id();
|
||||
} else {
|
||||
t_name = "startup_code";
|
||||
t_id = 0;
|
||||
}
|
||||
poco_warning(App_.logger(), fmt::format("std::exception in {}: {} thr_id={}",
|
||||
t_name,E.what(),
|
||||
t_id));
|
||||
}
|
||||
|
||||
inline void exception() override {
|
||||
if(Poco::Thread::current()!= nullptr) {
|
||||
t_name = Poco::Thread::current()->getName();
|
||||
t_id = Poco::Thread::current()->id();
|
||||
} else {
|
||||
t_name = "startup_code";
|
||||
t_id = 0;
|
||||
}
|
||||
poco_warning(App_.logger(), fmt::format("generic exception in {} thr_id={}",
|
||||
t_name, t_id));
|
||||
}
|
||||
private:
|
||||
Poco::Util::Application &App_;
|
||||
std::string t_name;
|
||||
int t_id=0;
|
||||
};
|
||||
|
||||
}
|
||||
132
src/framework/MicroServiceExtra.h
Normal file
132
src/framework/MicroServiceExtra.h
Normal file
@@ -0,0 +1,132 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-26.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include "Poco/BasicEvent.h"
|
||||
#include "Poco/ExpireLRUCache.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class ConfigurationEntry {
|
||||
public:
|
||||
template <typename T> explicit ConfigurationEntry(T def) :
|
||||
Default_(def),
|
||||
Current_(def){
|
||||
}
|
||||
|
||||
template <typename T> explicit ConfigurationEntry(T def, T cur, const std::string &Hint="") :
|
||||
Default_(def),
|
||||
Current_(cur),
|
||||
Hint_(Hint){
|
||||
}
|
||||
|
||||
inline ConfigurationEntry()=default;
|
||||
inline ~ConfigurationEntry()=default;
|
||||
|
||||
template <typename T> explicit operator T () const { return std::get<T>(Current_); }
|
||||
inline ConfigurationEntry & operator=(const char *v) { Current_ = std::string(v); return *this;}
|
||||
template <typename T> ConfigurationEntry & operator=(T v) { Current_ = (T) v; return *this;}
|
||||
|
||||
void reset() {
|
||||
Current_ = Default_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::variant<bool,uint64_t,std::string> Default_, Current_;
|
||||
std::string Hint_;
|
||||
};
|
||||
inline std::string to_string(const ConfigurationEntry &v) { return (std::string) v; }
|
||||
|
||||
typedef std::map<std::string,ConfigurationEntry> ConfigurationMap_t;
|
||||
|
||||
template <typename T> class FIFO {
|
||||
public:
|
||||
explicit FIFO(uint32_t Size) :
|
||||
Size_(Size) {
|
||||
Buffer_ = new T [Size_];
|
||||
}
|
||||
|
||||
~FIFO() {
|
||||
delete [] Buffer_;
|
||||
}
|
||||
|
||||
mutable Poco::BasicEvent<bool> Writable_;
|
||||
mutable Poco::BasicEvent<bool> Readable_;
|
||||
|
||||
inline bool Read(T &t) {
|
||||
{
|
||||
std::lock_guard M(Mutex_);
|
||||
if (Write_ == Read_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
t = Buffer_[Read_++];
|
||||
if (Read_ == Size_) {
|
||||
Read_ = 0;
|
||||
}
|
||||
Used_--;
|
||||
}
|
||||
bool flag = true;
|
||||
Writable_.notify(this, flag);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool Write(const T &t) {
|
||||
{
|
||||
std::lock_guard M(Mutex_);
|
||||
|
||||
Buffer_[Write_++] = t;
|
||||
if (Write_ == Size_) {
|
||||
Write_ = 0;
|
||||
}
|
||||
Used_++;
|
||||
MaxEverUsed_ = std::max(Used_,MaxEverUsed_);
|
||||
}
|
||||
bool flag = true;
|
||||
Readable_.notify(this, flag);
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool isFull() {
|
||||
std::lock_guard M(Mutex_);
|
||||
return Used_==Buffer_->capacity();
|
||||
}
|
||||
|
||||
inline auto MaxEverUser() const { return MaxEverUsed_; }
|
||||
|
||||
private:
|
||||
std::recursive_mutex Mutex_;
|
||||
uint32_t Size_=0;
|
||||
uint32_t Read_=0;
|
||||
uint32_t Write_=0;
|
||||
uint32_t Used_=0;
|
||||
uint32_t MaxEverUsed_=0;
|
||||
T * Buffer_ = nullptr;
|
||||
};
|
||||
|
||||
template <class Record, typename KeyType = std::string, int Size=256, int Expiry=60000> class RecordCache {
|
||||
public:
|
||||
explicit RecordCache( KeyType Record::* Q) :
|
||||
MemberOffset(Q){
|
||||
};
|
||||
inline auto update(const Record &R) {
|
||||
return Cache_.update(R.*MemberOffset, R);
|
||||
}
|
||||
inline auto get(const KeyType &K) {
|
||||
return Cache_.get(K);
|
||||
}
|
||||
inline auto remove(const KeyType &K) {
|
||||
return Cache_.remove(K);
|
||||
}
|
||||
inline auto remove(const Record &R) {
|
||||
return Cache_.remove(R.*MemberOffset);
|
||||
}
|
||||
private:
|
||||
KeyType Record::* MemberOffset;
|
||||
Poco::ExpireLRUCache<KeyType,Record> Cache_{Size,Expiry};
|
||||
};
|
||||
}
|
||||
121
src/framework/MicroServiceFuncs.cpp
Normal file
121
src/framework/MicroServiceFuncs.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#include "framework/MicroService.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
const std::string &MicroServiceDataDirectory() { return MicroService::instance().DataDir(); }
|
||||
|
||||
Types::MicroServiceMetaVec MicroServiceGetServices(const std::string &Type) {
|
||||
return MicroService::instance().GetServices(Type);
|
||||
}
|
||||
|
||||
Types::MicroServiceMetaVec MicroServiceGetServices() {
|
||||
return MicroService::instance().GetServices();
|
||||
}
|
||||
|
||||
std::string MicroServicePublicEndPoint() { return MicroService::instance().PublicEndPoint(); }
|
||||
|
||||
std::string MicroServiceConfigGetString(const std::string &Key, const std::string &DefaultValue) {
|
||||
return MicroService::instance().ConfigGetString(Key, DefaultValue);
|
||||
}
|
||||
|
||||
bool MicroServiceConfigGetBool(const std::string &Key, bool DefaultValue) {
|
||||
return MicroService::instance().ConfigGetBool(Key, DefaultValue);
|
||||
}
|
||||
|
||||
std::uint64_t MicroServiceConfigGetInt(const std::string &Key, std::uint64_t DefaultValue) {
|
||||
return MicroService::instance().ConfigGetInt(Key, DefaultValue);
|
||||
}
|
||||
|
||||
std::string MicroServicePrivateEndPoint() { return MicroService::instance().PrivateEndPoint(); }
|
||||
|
||||
std::uint64_t MicroServiceID() { return MicroService::instance().ID(); }
|
||||
|
||||
bool MicroServiceIsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request) {
|
||||
return MicroService::instance().IsValidAPIKEY(Request);
|
||||
}
|
||||
|
||||
bool MicroServiceNoAPISecurity() { return MicroService::instance().NoAPISecurity(); }
|
||||
|
||||
void MicroServiceLoadConfigurationFile() { MicroService::instance().LoadConfigurationFile(); }
|
||||
|
||||
void MicroServiceReload() { MicroService::instance().Reload(); }
|
||||
|
||||
void MicroServiceReload(const std::string &Type) { MicroService::instance().Reload(Type); }
|
||||
|
||||
const Types::StringVec MicroServiceGetLogLevelNames() {
|
||||
return MicroService::instance().GetLogLevelNames();
|
||||
}
|
||||
|
||||
const Types::StringVec MicroServiceGetSubSystems() {
|
||||
return MicroService::instance().GetSubSystems();
|
||||
}
|
||||
|
||||
Types::StringPairVec MicroServiceGetLogLevels() { return MicroService::instance().GetLogLevels(); }
|
||||
|
||||
bool MicroServiceSetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level) {
|
||||
return MicroService::instance().SetSubsystemLogLevel(SubSystem, Level);
|
||||
}
|
||||
|
||||
void MicroServiceGetExtraConfiguration(Poco::JSON::Object &Answer) {
|
||||
MicroService::instance().GetExtraConfiguration(Answer);
|
||||
}
|
||||
|
||||
std::string MicroServiceVersion() { return MicroService::instance().Version(); }
|
||||
|
||||
std::uint64_t MicroServiceUptimeTotalSeconds() {
|
||||
return MicroService::instance().uptime().totalSeconds();
|
||||
}
|
||||
|
||||
std::uint64_t MicroServiceStartTimeEpochTime() {
|
||||
return MicroService::instance().startTime().epochTime();
|
||||
}
|
||||
|
||||
std::string MicroServiceGetUIURI() { return MicroService::instance().GetUIURI(); }
|
||||
|
||||
const SubSystemVec MicroServiceGetFullSubSystems() {
|
||||
return MicroService::instance().GetFullSubSystems();
|
||||
}
|
||||
|
||||
std::string MicroServiceCreateUUID() { return MicroService::CreateUUID(); }
|
||||
|
||||
std::uint64_t MicroServiceDaemonBusTimer() { return MicroService::instance().DaemonBusTimer(); }
|
||||
|
||||
std::string MicroServiceMakeSystemEventMessage(const std::string &Type) {
|
||||
return MicroService::instance().MakeSystemEventMessage(Type);
|
||||
}
|
||||
|
||||
Poco::ThreadPool &MicroServiceTimerPool() { return MicroService::instance().TimerPool(); }
|
||||
|
||||
std::string MicroServiceConfigPath(const std::string &Key,
|
||||
const std::string &DefaultValue) {
|
||||
return MicroService::instance().ConfigPath(Key, DefaultValue);
|
||||
}
|
||||
|
||||
std::string MicroServiceWWWAssetsDir() {
|
||||
return MicroService::instance().WWWAssetsDir();
|
||||
}
|
||||
|
||||
std::uint64_t MicroServiceRandom(std::uint64_t Start,std::uint64_t End) {
|
||||
return MicroService::instance().Random(Start, End);
|
||||
}
|
||||
|
||||
std::uint64_t MicroServiceRandom(std::uint64_t Range) {
|
||||
return MicroService::instance().Random(Range);
|
||||
}
|
||||
|
||||
std::string MicroServiceSign(Poco::JWT::Token &T, const std::string &Algo) {
|
||||
return MicroService::instance().Sign(T, Algo);
|
||||
}
|
||||
|
||||
std::string MicroServiceGetPublicAPIEndPoint() {
|
||||
return MicroService::instance().GetPublicAPIEndPoint();
|
||||
}
|
||||
|
||||
void MicroServiceDeleteOverrideConfiguration() {
|
||||
return MicroService::instance().DeleteOverrideConfiguration();
|
||||
}
|
||||
}
|
||||
56
src/framework/MicroServiceFuncs.h
Normal file
56
src/framework/MicroServiceFuncs.h
Normal file
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
|
||||
#include "Poco/Net/HTTPServerRequest.h"
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "Poco/ThreadPool.h"
|
||||
#include "Poco/JWT/Token.h"
|
||||
|
||||
|
||||
namespace OpenWifi {
|
||||
class SubSystemServer;
|
||||
using SubSystemVec=std::vector<SubSystemServer *>;
|
||||
const std::string & MicroServiceDataDirectory();
|
||||
Types::MicroServiceMetaVec MicroServiceGetServices(const std::string & Type);
|
||||
Types::MicroServiceMetaVec MicroServiceGetServices();
|
||||
std::string MicroServicePublicEndPoint();
|
||||
std::string MicroServiceConfigGetString(const std::string &Key, const std::string &DefaultValue);
|
||||
bool MicroServiceConfigGetBool(const std::string &Key, bool DefaultValue);
|
||||
std::uint64_t MicroServiceConfigGetInt(const std::string &Key, std::uint64_t DefaultValue);
|
||||
std::string MicroServicePrivateEndPoint();
|
||||
std::uint64_t MicroServiceID();
|
||||
bool MicroServiceIsValidAPIKEY(const Poco::Net::HTTPServerRequest &Request);
|
||||
bool MicroServiceNoAPISecurity();
|
||||
void MicroServiceLoadConfigurationFile();
|
||||
void MicroServiceReload();
|
||||
void MicroServiceReload(const std::string &Type);
|
||||
const Types::StringVec MicroServiceGetLogLevelNames();
|
||||
const Types::StringVec MicroServiceGetSubSystems();
|
||||
Types::StringPairVec MicroServiceGetLogLevels();
|
||||
bool MicroServiceSetSubsystemLogLevel(const std::string &SubSystem, const std::string &Level);
|
||||
void MicroServiceGetExtraConfiguration(Poco::JSON::Object &Answer);
|
||||
std::string MicroServiceVersion();
|
||||
std::uint64_t MicroServiceUptimeTotalSeconds();
|
||||
std::uint64_t MicroServiceStartTimeEpochTime();
|
||||
std::string MicroServiceGetUIURI();
|
||||
const SubSystemVec MicroServiceGetFullSubSystems();
|
||||
std::string MicroServiceCreateUUID();
|
||||
std::uint64_t MicroServiceDaemonBusTimer();
|
||||
std::string MicroServiceMakeSystemEventMessage( const std::string & Type );
|
||||
Poco::ThreadPool & MicroServiceTimerPool();
|
||||
std::string MicroServiceConfigPath(const std::string &Key,
|
||||
const std::string &DefaultValue);
|
||||
std::string MicroServiceWWWAssetsDir();
|
||||
std::uint64_t MicroServiceRandom(std::uint64_t Start,std::uint64_t End);
|
||||
std::uint64_t MicroServiceRandom(std::uint64_t Range);
|
||||
std::string MicroServiceSign(Poco::JWT::Token &T, const std::string &Algo);
|
||||
std::string MicroServiceGetPublicAPIEndPoint();
|
||||
void MicroServiceDeleteOverrideConfiguration();
|
||||
}
|
||||
22
src/framework/MicroServiceNames.h
Normal file
22
src/framework/MicroServiceNames.h
Normal file
@@ -0,0 +1,22 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
static const std::string uSERVICE_SECURITY{"owsec"};
|
||||
static const std::string uSERVICE_GATEWAY{"owgw"};
|
||||
static const std::string uSERVICE_FIRMWARE{ "owfms"};
|
||||
static const std::string uSERVICE_TOPOLOGY{ "owtopo"};
|
||||
static const std::string uSERVICE_PROVISIONING{ "owprov"};
|
||||
static const std::string uSERVICE_OWLS{ "owls"};
|
||||
static const std::string uSERVICE_SUBCRIBER{ "owsub"};
|
||||
static const std::string uSERVICE_INSTALLER{ "owinst"};
|
||||
static const std::string uSERVICE_ANALYTICS{ "owanalytics"};
|
||||
static const std::string uSERVICE_OWRRM{ "owrrm"};
|
||||
|
||||
}
|
||||
289
src/framework/OpenAPIRequests.cpp
Normal file
289
src/framework/OpenAPIRequests.cpp
Normal file
@@ -0,0 +1,289 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#include "OpenAPIRequests.h"
|
||||
|
||||
#include "Poco/Logger.h"
|
||||
#include "Poco/URI.h"
|
||||
#include "Poco/Net/HTTPRequest.h"
|
||||
#include "Poco/Net/HTTPSClientSession.h"
|
||||
#include "Poco/JSON/Parser.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestGet::Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken) {
|
||||
try {
|
||||
|
||||
auto Services = MicroServiceGetServices(Type_);
|
||||
for(auto const &Svc:Services) {
|
||||
Poco::URI URI(Svc.PrivateEndPoint);
|
||||
|
||||
auto Secure = (URI.getScheme() == "https");
|
||||
|
||||
URI.setPath(EndPoint_);
|
||||
for (const auto &qp : QueryData_)
|
||||
URI.addQueryParameter(qp.first, qp.second);
|
||||
|
||||
std::string Path(URI.getPathAndQuery());
|
||||
Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_GET,
|
||||
Path,
|
||||
Poco::Net::HTTPMessage::HTTP_1_1);
|
||||
|
||||
poco_debug(Poco::Logger::get("REST-CALLER-GET"), fmt::format(" {}", LoggingStr_.empty() ? URI.toString() : LoggingStr_ ) );
|
||||
|
||||
if(BearerToken.empty()) {
|
||||
Request.add("X-API-KEY", Svc.AccessKey);
|
||||
Request.add("X-INTERNAL-NAME", MicroServicePublicEndPoint());
|
||||
} else {
|
||||
// Authorization: Bearer ${token}
|
||||
Request.add("Authorization", "Bearer " + BearerToken);
|
||||
}
|
||||
|
||||
if(Secure) {
|
||||
Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort());
|
||||
Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000));
|
||||
|
||||
Session.sendRequest(Request);
|
||||
|
||||
Poco::Net::HTTPResponse Response;
|
||||
std::istream &is = Session.receiveResponse(Response);
|
||||
if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) {
|
||||
Poco::JSON::Parser P;
|
||||
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
|
||||
}
|
||||
return Response.getStatus();
|
||||
} else {
|
||||
Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort());
|
||||
Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000));
|
||||
|
||||
Session.sendRequest(Request);
|
||||
|
||||
Poco::Net::HTTPResponse Response;
|
||||
std::istream &is = Session.receiveResponse(Response);
|
||||
if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) {
|
||||
Poco::JSON::Parser P;
|
||||
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
|
||||
}
|
||||
return Response.getStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const Poco::Exception &E)
|
||||
{
|
||||
Poco::Logger::get("REST-CALLER-GET").log(E);
|
||||
}
|
||||
return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT;
|
||||
}
|
||||
|
||||
Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestPut::Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken) {
|
||||
try {
|
||||
auto Services = MicroServiceGetServices(Type_);
|
||||
for(auto const &Svc:Services) {
|
||||
Poco::URI URI(Svc.PrivateEndPoint);
|
||||
|
||||
auto Secure = (URI.getScheme() == "https");
|
||||
|
||||
URI.setPath(EndPoint_);
|
||||
for (const auto &qp : QueryData_)
|
||||
URI.addQueryParameter(qp.first, qp.second);
|
||||
|
||||
poco_debug(Poco::Logger::get("REST-CALLER-PUT"), fmt::format(" {}", LoggingStr_.empty() ? URI.toString() : LoggingStr_ ) );
|
||||
|
||||
std::string Path(URI.getPathAndQuery());
|
||||
|
||||
Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_PUT,
|
||||
Path,
|
||||
Poco::Net::HTTPMessage::HTTP_1_1);
|
||||
std::ostringstream obody;
|
||||
Poco::JSON::Stringifier::stringify(Body_,obody);
|
||||
|
||||
Request.setContentType("application/json");
|
||||
Request.setContentLength(obody.str().size());
|
||||
|
||||
if(BearerToken.empty()) {
|
||||
Request.add("X-API-KEY", Svc.AccessKey);
|
||||
Request.add("X-INTERNAL-NAME", MicroServicePublicEndPoint());
|
||||
} else {
|
||||
// Authorization: Bearer ${token}
|
||||
Request.add("Authorization", "Bearer " + BearerToken);
|
||||
}
|
||||
|
||||
if(Secure) {
|
||||
Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort());
|
||||
Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000));
|
||||
|
||||
std::ostream &os = Session.sendRequest(Request);
|
||||
os << obody.str();
|
||||
|
||||
Poco::Net::HTTPResponse Response;
|
||||
std::istream &is = Session.receiveResponse(Response);
|
||||
if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) {
|
||||
Poco::JSON::Parser P;
|
||||
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
|
||||
} else {
|
||||
Poco::JSON::Parser P;
|
||||
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
|
||||
}
|
||||
return Response.getStatus();
|
||||
} else {
|
||||
Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort());
|
||||
Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000));
|
||||
|
||||
std::ostream &os = Session.sendRequest(Request);
|
||||
os << obody.str();
|
||||
|
||||
Poco::Net::HTTPResponse Response;
|
||||
std::istream &is = Session.receiveResponse(Response);
|
||||
if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) {
|
||||
Poco::JSON::Parser P;
|
||||
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
|
||||
} else {
|
||||
Poco::JSON::Parser P;
|
||||
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
|
||||
}
|
||||
return Response.getStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const Poco::Exception &E)
|
||||
{
|
||||
Poco::Logger::get("REST-CALLER-PUT").log(E);
|
||||
}
|
||||
return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT;
|
||||
}
|
||||
|
||||
Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestPost::Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken) {
|
||||
try {
|
||||
auto Services = MicroServiceGetServices(Type_);
|
||||
|
||||
for(auto const &Svc:Services) {
|
||||
Poco::URI URI(Svc.PrivateEndPoint);
|
||||
|
||||
|
||||
auto Secure = (URI.getScheme() == "https");
|
||||
|
||||
URI.setPath(EndPoint_);
|
||||
for (const auto &qp : QueryData_)
|
||||
URI.addQueryParameter(qp.first, qp.second);
|
||||
|
||||
poco_debug(Poco::Logger::get("REST-CALLER-POST"),fmt::format(" {}", LoggingStr_.empty() ? URI.toString() : LoggingStr_ ) );
|
||||
|
||||
std::string Path(URI.getPathAndQuery());
|
||||
|
||||
Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_POST,
|
||||
Path,
|
||||
Poco::Net::HTTPMessage::HTTP_1_1);
|
||||
std::ostringstream obody;
|
||||
Poco::JSON::Stringifier::stringify(Body_,obody);
|
||||
|
||||
Request.setContentType("application/json");
|
||||
Request.setContentLength(obody.str().size());
|
||||
|
||||
if(BearerToken.empty()) {
|
||||
Request.add("X-API-KEY", Svc.AccessKey);
|
||||
Request.add("X-INTERNAL-NAME", MicroServicePublicEndPoint());
|
||||
} else {
|
||||
// Authorization: Bearer ${token}
|
||||
Request.add("Authorization", "Bearer " + BearerToken);
|
||||
}
|
||||
|
||||
if(Secure) {
|
||||
Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort());
|
||||
Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000));
|
||||
std::ostream &os = Session.sendRequest(Request);
|
||||
os << obody.str();
|
||||
|
||||
Poco::Net::HTTPResponse Response;
|
||||
std::istream &is = Session.receiveResponse(Response);
|
||||
if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) {
|
||||
Poco::JSON::Parser P;
|
||||
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
|
||||
} else {
|
||||
Poco::JSON::Parser P;
|
||||
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
|
||||
}
|
||||
return Response.getStatus();
|
||||
} else {
|
||||
Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort());
|
||||
Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000));
|
||||
std::ostream &os = Session.sendRequest(Request);
|
||||
os << obody.str();
|
||||
|
||||
Poco::Net::HTTPResponse Response;
|
||||
std::istream &is = Session.receiveResponse(Response);
|
||||
if (Response.getStatus() == Poco::Net::HTTPResponse::HTTP_OK) {
|
||||
Poco::JSON::Parser P;
|
||||
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
|
||||
} else {
|
||||
Poco::JSON::Parser P;
|
||||
ResponseObject = P.parse(is).extract<Poco::JSON::Object::Ptr>();
|
||||
}
|
||||
return Response.getStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const Poco::Exception &E)
|
||||
{
|
||||
Poco::Logger::get("REST-CALLER-POST").log(E);
|
||||
}
|
||||
return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT;
|
||||
}
|
||||
|
||||
Poco::Net::HTTPServerResponse::HTTPStatus OpenAPIRequestDelete::Do(const std::string & BearerToken) {
|
||||
try {
|
||||
auto Services = MicroServiceGetServices(Type_);
|
||||
|
||||
for(auto const &Svc:Services) {
|
||||
Poco::URI URI(Svc.PrivateEndPoint);
|
||||
|
||||
auto Secure = (URI.getScheme() == "https");
|
||||
|
||||
URI.setPath(EndPoint_);
|
||||
for (const auto &qp : QueryData_)
|
||||
URI.addQueryParameter(qp.first, qp.second);
|
||||
|
||||
poco_debug(Poco::Logger::get("REST-CALLER-DELETE"),fmt::format(" {}", LoggingStr_.empty() ? URI.toString() : LoggingStr_ ) );
|
||||
|
||||
std::string Path(URI.getPathAndQuery());
|
||||
|
||||
Poco::Net::HTTPRequest Request(Poco::Net::HTTPRequest::HTTP_DELETE,
|
||||
Path,
|
||||
Poco::Net::HTTPMessage::HTTP_1_1);
|
||||
if(BearerToken.empty()) {
|
||||
Request.add("X-API-KEY", Svc.AccessKey);
|
||||
Request.add("X-INTERNAL-NAME", MicroServicePublicEndPoint());
|
||||
} else {
|
||||
// Authorization: Bearer ${token}
|
||||
Request.add("Authorization", "Bearer " + BearerToken);
|
||||
}
|
||||
|
||||
if(Secure) {
|
||||
Poco::Net::HTTPSClientSession Session(URI.getHost(), URI.getPort());
|
||||
Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000));
|
||||
Session.sendRequest(Request);
|
||||
Poco::Net::HTTPResponse Response;
|
||||
Session.receiveResponse(Response);
|
||||
return Response.getStatus();
|
||||
} else {
|
||||
Poco::Net::HTTPClientSession Session(URI.getHost(), URI.getPort());
|
||||
Session.setTimeout(Poco::Timespan(msTimeout_ / 1000, msTimeout_ % 1000));
|
||||
Session.sendRequest(Request);
|
||||
Poco::Net::HTTPResponse Response;
|
||||
Session.receiveResponse(Response);
|
||||
return Response.getStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const Poco::Exception &E)
|
||||
{
|
||||
Poco::Logger::get("REST-CALLER-DELETE").log(E);
|
||||
}
|
||||
return Poco::Net::HTTPServerResponse::HTTP_GATEWAY_TIMEOUT;
|
||||
}
|
||||
|
||||
|
||||
} // namespace OpenWifi
|
||||
110
src/framework/OpenAPIRequests.h
Normal file
110
src/framework/OpenAPIRequests.h
Normal file
@@ -0,0 +1,110 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "Poco/Net/HTTPServerResponse.h"
|
||||
|
||||
#include "framework/OpenWifiTypes.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class OpenAPIRequestGet {
|
||||
public:
|
||||
explicit OpenAPIRequestGet( const std::string & Type,
|
||||
const std::string & EndPoint,
|
||||
const Types::StringPairVec & QueryData,
|
||||
uint64_t msTimeout,
|
||||
const std::string &LoggingStr=""):
|
||||
Type_(Type),
|
||||
EndPoint_(EndPoint),
|
||||
QueryData_(QueryData),
|
||||
msTimeout_(msTimeout),
|
||||
LoggingStr_(LoggingStr){};
|
||||
Poco::Net::HTTPServerResponse::HTTPStatus Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken = "");
|
||||
private:
|
||||
std::string Type_;
|
||||
std::string EndPoint_;
|
||||
Types::StringPairVec QueryData_;
|
||||
uint64_t msTimeout_;
|
||||
std::string LoggingStr_;
|
||||
};
|
||||
|
||||
class OpenAPIRequestPut {
|
||||
public:
|
||||
explicit OpenAPIRequestPut( const std::string & Type,
|
||||
const std::string & EndPoint,
|
||||
const Types::StringPairVec & QueryData,
|
||||
const Poco::JSON::Object & Body,
|
||||
uint64_t msTimeout,
|
||||
const std::string &LoggingStr=""):
|
||||
Type_(Type),
|
||||
EndPoint_(EndPoint),
|
||||
QueryData_(QueryData),
|
||||
msTimeout_(msTimeout),
|
||||
Body_(Body),
|
||||
LoggingStr_(LoggingStr){};
|
||||
|
||||
Poco::Net::HTTPServerResponse::HTTPStatus Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken = "");
|
||||
|
||||
private:
|
||||
std::string Type_;
|
||||
std::string EndPoint_;
|
||||
Types::StringPairVec QueryData_;
|
||||
uint64_t msTimeout_;
|
||||
Poco::JSON::Object Body_;
|
||||
std::string LoggingStr_;
|
||||
};
|
||||
|
||||
class OpenAPIRequestPost {
|
||||
public:
|
||||
explicit OpenAPIRequestPost( const std::string & Type,
|
||||
const std::string & EndPoint,
|
||||
const Types::StringPairVec & QueryData,
|
||||
const Poco::JSON::Object & Body,
|
||||
uint64_t msTimeout,
|
||||
const std::string &LoggingStr=""):
|
||||
Type_(Type),
|
||||
EndPoint_(EndPoint),
|
||||
QueryData_(QueryData),
|
||||
msTimeout_(msTimeout),
|
||||
Body_(Body),
|
||||
LoggingStr_(LoggingStr){};
|
||||
Poco::Net::HTTPServerResponse::HTTPStatus Do(Poco::JSON::Object::Ptr &ResponseObject, const std::string & BearerToken = "");
|
||||
private:
|
||||
std::string Type_;
|
||||
std::string EndPoint_;
|
||||
Types::StringPairVec QueryData_;
|
||||
uint64_t msTimeout_;
|
||||
Poco::JSON::Object Body_;
|
||||
std::string LoggingStr_;
|
||||
};
|
||||
|
||||
class OpenAPIRequestDelete {
|
||||
public:
|
||||
explicit OpenAPIRequestDelete( const std::string & Type,
|
||||
const std::string & EndPoint,
|
||||
const Types::StringPairVec & QueryData,
|
||||
uint64_t msTimeout,
|
||||
const std::string &LoggingStr=""):
|
||||
Type_(Type),
|
||||
EndPoint_(EndPoint),
|
||||
QueryData_(QueryData),
|
||||
msTimeout_(msTimeout),
|
||||
LoggingStr_(LoggingStr){};
|
||||
Poco::Net::HTTPServerResponse::HTTPStatus Do(const std::string & BearerToken = "");
|
||||
|
||||
private:
|
||||
std::string Type_;
|
||||
std::string EndPoint_;
|
||||
Types::StringPairVec QueryData_;
|
||||
uint64_t msTimeout_;
|
||||
Poco::JSON::Object Body_;
|
||||
std::string LoggingStr_;
|
||||
};
|
||||
|
||||
} // namespace OpenWifi
|
||||
@@ -28,6 +28,19 @@ namespace OpenWifi::Types {
|
||||
typedef std::string UUID_t;
|
||||
typedef std::vector<UUID_t> UUIDvec_t;
|
||||
typedef std::map<std::string,std::map<uint32_t,uint64_t>> Counted3DMapSII;
|
||||
|
||||
struct MicroServiceMeta {
|
||||
uint64_t Id=0;
|
||||
std::string Type;
|
||||
std::string PrivateEndPoint;
|
||||
std::string PublicEndPoint;
|
||||
std::string AccessKey;
|
||||
std::string Version;
|
||||
uint64_t LastUpdate=0;
|
||||
};
|
||||
|
||||
typedef std::map<std::string, MicroServiceMeta> MicroServiceMetaMap;
|
||||
typedef std::vector<MicroServiceMeta> MicroServiceMetaVec;
|
||||
}
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
27
src/framework/RESTAPI_ExtServer.cpp
Normal file
27
src/framework/RESTAPI_ExtServer.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#include "framework/RESTAPI_ExtServer.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
Poco::Net::HTTPRequestHandler *ExtRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest &Request) {
|
||||
try {
|
||||
Poco::URI uri(Request.getURI());
|
||||
auto TID = NextTransactionId_++;
|
||||
Utils::SetThreadName(fmt::format("x-rest:{}",TID).c_str());
|
||||
return RESTAPI_ExtServer()->CallServer(uri.getPath(), TID);
|
||||
} catch (...) {
|
||||
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Poco::Net::HTTPRequestHandler *RESTAPI_ExtServer::CallServer(const std::string &Path, uint64_t Id) {
|
||||
RESTAPIHandler::BindingMap Bindings;
|
||||
Utils::SetThreadName(fmt::format("x-rest:{}",Id).c_str());
|
||||
return RESTAPI_ExtRouter(Path, Bindings, Logger(), Server_, Id);
|
||||
}
|
||||
|
||||
}
|
||||
99
src/framework/RESTAPI_ExtServer.h
Normal file
99
src/framework/RESTAPI_ExtServer.h
Normal file
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Poco/Net/HTTPServer.h"
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
Poco::Net::HTTPRequestHandler * RESTAPI_ExtRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings,
|
||||
Poco::Logger & L, RESTAPI_GenericServerAccounting & S, uint64_t Id);
|
||||
|
||||
class ExtRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
|
||||
public:
|
||||
ExtRequestHandlerFactory() = default;
|
||||
Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override;
|
||||
private:
|
||||
static inline std::atomic_uint64_t NextTransactionId_ = 1;
|
||||
};
|
||||
|
||||
class RESTAPI_ExtServer : public SubSystemServer {
|
||||
public:
|
||||
static auto instance() {
|
||||
static auto instance_ = new RESTAPI_ExtServer;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
inline int Start() override {
|
||||
poco_information(Logger(),"Starting.");
|
||||
Server_.InitLogging();
|
||||
|
||||
for(const auto & Svr: ConfigServersList_) {
|
||||
|
||||
if(MicroServiceNoAPISecurity()) {
|
||||
poco_information(Logger(),fmt::format("Starting: {}:{}. Security has been disabled for APIs.", Svr.Address(), Svr.Port()));
|
||||
} else {
|
||||
poco_information(Logger(),fmt::format("Starting: {}:{} Keyfile:{} CertFile: {}", Svr.Address(), Svr.Port(),
|
||||
Svr.KeyFile(),Svr.CertFile()));
|
||||
Svr.LogCert(Logger());
|
||||
if (!Svr.RootCA().empty())
|
||||
Svr.LogCas(Logger());
|
||||
}
|
||||
|
||||
Poco::Net::HTTPServerParams::Ptr Params = new Poco::Net::HTTPServerParams;
|
||||
Params->setKeepAlive(true);
|
||||
Params->setName("ws:xrest");
|
||||
|
||||
std::unique_ptr<Poco::Net::HTTPServer> NewServer;
|
||||
if(MicroServiceNoAPISecurity()) {
|
||||
auto Sock{Svr.CreateSocket(Logger())};
|
||||
NewServer = std::make_unique<Poco::Net::HTTPServer>(new ExtRequestHandlerFactory, Pool_, Sock, Params);
|
||||
} else {
|
||||
auto Sock{Svr.CreateSecureSocket(Logger())};
|
||||
NewServer = std::make_unique<Poco::Net::HTTPServer>(new ExtRequestHandlerFactory, Pool_, Sock, Params);
|
||||
};
|
||||
NewServer->start();
|
||||
RESTServers_.push_back(std::move(NewServer));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void Stop() override {
|
||||
poco_information(Logger(),"Stopping...");
|
||||
for( const auto & svr : RESTServers_ )
|
||||
svr->stopAll(true);
|
||||
Pool_.stopAll();
|
||||
Pool_.joinAll();
|
||||
RESTServers_.clear();
|
||||
poco_information(Logger(),"Stopped...");
|
||||
}
|
||||
|
||||
inline void reinitialize([[maybe_unused]] Poco::Util::Application &self) override {
|
||||
MicroServiceLoadConfigurationFile();
|
||||
poco_information(Logger(),"Reinitializing.");
|
||||
Stop();
|
||||
Start();
|
||||
}
|
||||
|
||||
Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id);
|
||||
const Poco::ThreadPool & Pool() { return Pool_; }
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<Poco::Net::HTTPServer>> RESTServers_;
|
||||
Poco::ThreadPool Pool_{"x-rest",8,128};
|
||||
RESTAPI_GenericServerAccounting Server_;
|
||||
|
||||
RESTAPI_ExtServer() noexcept:
|
||||
SubSystemServer("RESTAPI_ExtServer", "REST-XSRV", "openwifi.restapi")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline auto RESTAPI_ExtServer() { return RESTAPI_ExtServer::instance(); };
|
||||
|
||||
}
|
||||
@@ -1,299 +0,0 @@
|
||||
//
|
||||
// License type: BSD 3-Clause License
|
||||
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
|
||||
//
|
||||
// Created by Stephane Bourque on 2021-03-04.
|
||||
// Arilia Wireless Inc.
|
||||
//
|
||||
|
||||
#include "Poco/JSON/Parser.h"
|
||||
#include "Poco/JSON/Stringifier.h"
|
||||
|
||||
#include "Daemon.h"
|
||||
#ifdef TIP_GATEWAY_SERVICE
|
||||
#include "DeviceRegistry.h"
|
||||
#include "CapabilitiesCache.h"
|
||||
#endif
|
||||
|
||||
#include "RESTAPI_GWobjects.h"
|
||||
#include "framework/MicroService.h"
|
||||
|
||||
using OpenWifi::RESTAPI_utils::field_to_json;
|
||||
using OpenWifi::RESTAPI_utils::field_from_json;
|
||||
using OpenWifi::RESTAPI_utils::EmbedDocument;
|
||||
|
||||
namespace OpenWifi::GWObjects {
|
||||
|
||||
void Device::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"serialNumber", SerialNumber);
|
||||
#ifdef TIP_GATEWAY_SERVICE
|
||||
field_to_json(Obj,"deviceType", CapabilitiesCache::instance()->GetPlatform(Compatible));
|
||||
#endif
|
||||
field_to_json(Obj,"macAddress", MACAddress);
|
||||
field_to_json(Obj,"manufacturer", Manufacturer);
|
||||
field_to_json(Obj,"UUID", UUID);
|
||||
EmbedDocument("configuration", Obj, Configuration);
|
||||
field_to_json(Obj,"notes", Notes);
|
||||
field_to_json(Obj,"createdTimestamp", CreationTimestamp);
|
||||
field_to_json(Obj,"lastConfigurationChange", LastConfigurationChange);
|
||||
field_to_json(Obj,"lastConfigurationDownload", LastConfigurationDownload);
|
||||
field_to_json(Obj,"lastFWUpdate", LastFWUpdate);
|
||||
field_to_json(Obj,"owner", Owner);
|
||||
field_to_json(Obj,"location", Location);
|
||||
field_to_json(Obj,"venue", Venue);
|
||||
field_to_json(Obj,"firmware", Firmware);
|
||||
field_to_json(Obj,"compatible", Compatible);
|
||||
field_to_json(Obj,"fwUpdatePolicy", FWUpdatePolicy);
|
||||
field_to_json(Obj,"devicePassword", DevicePassword);
|
||||
field_to_json(Obj,"subscriber", subscriber);
|
||||
field_to_json(Obj,"entity", entity);
|
||||
field_to_json(Obj,"modified", modified);
|
||||
field_to_json(Obj,"locale", locale);
|
||||
}
|
||||
|
||||
void Device::to_json_with_status(Poco::JSON::Object &Obj) const {
|
||||
to_json(Obj);
|
||||
|
||||
#ifdef TIP_GATEWAY_SERVICE
|
||||
ConnectionState ConState;
|
||||
|
||||
if (DeviceRegistry()->GetState(SerialNumber, ConState)) {
|
||||
ConState.to_json(Obj);
|
||||
} else {
|
||||
field_to_json(Obj,"ipAddress", "");
|
||||
field_to_json(Obj,"txBytes", (uint64_t) 0);
|
||||
field_to_json(Obj,"rxBytes", (uint64_t )0);
|
||||
field_to_json(Obj,"messageCount", (uint64_t )0);
|
||||
field_to_json(Obj,"connected", false);
|
||||
field_to_json(Obj,"lastContact", "");
|
||||
field_to_json(Obj,"verifiedCertificate", "NO_CERTIFICATE");
|
||||
field_to_json(Obj,"associations_2G", (uint64_t) 0);
|
||||
field_to_json(Obj,"associations_5G", (uint64_t) 0);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Device::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"serialNumber",SerialNumber);
|
||||
field_from_json(Obj,"deviceType",DeviceType);
|
||||
field_from_json(Obj,"macAddress",MACAddress);
|
||||
field_from_json(Obj,"configuration",Configuration);
|
||||
field_from_json(Obj,"notes",Notes);
|
||||
field_from_json(Obj,"manufacturer",Manufacturer);
|
||||
field_from_json(Obj,"owner",Owner);
|
||||
field_from_json(Obj,"location",Location);
|
||||
field_from_json(Obj,"venue",Venue);
|
||||
field_from_json(Obj,"compatible",Compatible);
|
||||
field_from_json(Obj,"subscriber", subscriber);
|
||||
field_from_json(Obj,"entity", entity);
|
||||
field_from_json(Obj,"locale", locale);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Device::Print() const {
|
||||
std::cout << "Device: " << SerialNumber << " DeviceType:" << DeviceType << " MACAddress:" << MACAddress << " Manufacturer:"
|
||||
<< Manufacturer << " " << Configuration << std::endl;
|
||||
}
|
||||
|
||||
void Statistics::to_json(Poco::JSON::Object &Obj) const {
|
||||
EmbedDocument("data", Obj, Data);
|
||||
field_to_json(Obj,"UUID", UUID);
|
||||
field_to_json(Obj,"recorded", Recorded);
|
||||
}
|
||||
|
||||
void Capabilities::to_json(Poco::JSON::Object &Obj) const {
|
||||
EmbedDocument("capabilities", Obj, Capabilities);
|
||||
field_to_json(Obj,"firstUpdate", FirstUpdate);
|
||||
field_to_json(Obj,"lastUpdate", LastUpdate);
|
||||
}
|
||||
|
||||
void DeviceLog::to_json(Poco::JSON::Object &Obj) const {
|
||||
EmbedDocument("data", Obj, Data);
|
||||
field_to_json(Obj,"log", Log);
|
||||
field_to_json(Obj,"severity", Severity);
|
||||
field_to_json(Obj,"recorded", Recorded);
|
||||
field_to_json(Obj,"logType", LogType);
|
||||
field_to_json(Obj,"UUID", UUID);
|
||||
}
|
||||
|
||||
void HealthCheck::to_json(Poco::JSON::Object &Obj) const {
|
||||
EmbedDocument("values", Obj, Data);
|
||||
field_to_json(Obj,"UUID", UUID);
|
||||
field_to_json(Obj,"sanity", Sanity);
|
||||
field_to_json(Obj,"recorded", Recorded);
|
||||
}
|
||||
|
||||
void DefaultConfiguration::to_json(Poco::JSON::Object &Obj) const {
|
||||
EmbedDocument("configuration", Obj, Configuration);
|
||||
field_to_json(Obj,"name", Name);
|
||||
field_to_json(Obj,"modelIds", Models);
|
||||
field_to_json(Obj,"description", Description);
|
||||
field_to_json(Obj,"created", Created);
|
||||
field_to_json(Obj,"lastModified", LastModified);
|
||||
}
|
||||
|
||||
void CommandDetails::to_json(Poco::JSON::Object &Obj) const {
|
||||
EmbedDocument("details", Obj, Details);
|
||||
EmbedDocument("results", Obj, Results);
|
||||
field_to_json(Obj,"UUID", UUID);
|
||||
field_to_json(Obj,"serialNumber", SerialNumber);
|
||||
field_to_json(Obj,"command", Command);
|
||||
field_to_json(Obj,"errorText", ErrorText);
|
||||
field_to_json(Obj,"submittedBy", SubmittedBy);
|
||||
field_to_json(Obj,"status", Status);
|
||||
field_to_json(Obj,"submitted", Submitted);
|
||||
field_to_json(Obj,"executed", Executed);
|
||||
field_to_json(Obj,"completed", Completed);
|
||||
field_to_json(Obj,"when", RunAt);
|
||||
field_to_json(Obj,"errorCode", ErrorCode);
|
||||
field_to_json(Obj,"custom", Custom);
|
||||
field_to_json(Obj,"waitingForFile", WaitingForFile);
|
||||
field_to_json(Obj,"attachFile", AttachDate);
|
||||
field_to_json(Obj,"executionTime", executionTime);
|
||||
}
|
||||
|
||||
bool DefaultConfiguration::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"name",Name);
|
||||
field_from_json(Obj,"configuration",Configuration);
|
||||
field_from_json(Obj,"modelIds",Models);
|
||||
field_from_json(Obj,"description",Description);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BlackListedDevice::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"serialNumber", serialNumber);
|
||||
field_to_json(Obj,"author", author);
|
||||
field_to_json(Obj,"reason", reason);
|
||||
field_to_json(Obj,"created", created);
|
||||
}
|
||||
|
||||
bool BlackListedDevice::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"serialNumber",serialNumber);
|
||||
field_from_json(Obj,"author",author);
|
||||
field_from_json(Obj,"reason",reason);
|
||||
field_from_json(Obj,"created",created);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void ConnectionState::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"ipAddress", Address);
|
||||
field_to_json(Obj,"txBytes", TX);
|
||||
field_to_json(Obj,"rxBytes", RX);
|
||||
field_to_json(Obj,"messageCount", MessageCount);
|
||||
field_to_json(Obj,"UUID", UUID);
|
||||
field_to_json(Obj,"connected", Connected);
|
||||
field_to_json(Obj,"firmware", Firmware);
|
||||
field_to_json(Obj,"lastContact", LastContact);
|
||||
field_to_json(Obj,"associations_2G", Associations_2G);
|
||||
field_to_json(Obj,"associations_5G", Associations_5G);
|
||||
field_to_json(Obj,"webSocketClients", webSocketClients);
|
||||
field_to_json(Obj,"websocketPackets", websocketPackets);
|
||||
field_to_json(Obj,"kafkaClients", kafkaClients);
|
||||
field_to_json(Obj,"kafkaPackets", kafkaPackets);
|
||||
field_to_json(Obj,"locale", locale);
|
||||
|
||||
switch(VerifiedCertificate) {
|
||||
case NO_CERTIFICATE:
|
||||
field_to_json(Obj,"verifiedCertificate", "NO_CERTIFICATE"); break;
|
||||
case VALID_CERTIFICATE:
|
||||
field_to_json(Obj,"verifiedCertificate", "VALID_CERTIFICATE"); break;
|
||||
case MISMATCH_SERIAL:
|
||||
field_to_json(Obj,"verifiedCertificate", "MISMATCH_SERIAL"); break;
|
||||
case VERIFIED:
|
||||
field_to_json(Obj,"verifiedCertificate", "VERIFIED"); break;
|
||||
default:
|
||||
field_to_json(Obj,"verifiedCertificate", "NO_CERTIFICATE"); break;
|
||||
}
|
||||
}
|
||||
|
||||
void RttySessionDetails::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"serialNumber", SerialNumber);
|
||||
field_to_json(Obj,"server", Server);
|
||||
field_to_json(Obj,"port", Port);
|
||||
field_to_json(Obj,"token",Token);
|
||||
field_to_json(Obj,"timeout", TimeOut);
|
||||
field_to_json(Obj,"connectionId",ConnectionId);
|
||||
field_to_json(Obj,"commandUUID",CommandUUID);
|
||||
field_to_json(Obj,"started", Started);
|
||||
field_to_json(Obj,"viewport",ViewPort);
|
||||
field_to_json(Obj,"password",DevicePassword);
|
||||
}
|
||||
|
||||
void Dashboard::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"commands",commands);
|
||||
field_to_json(Obj,"upTimes",upTimes);
|
||||
field_to_json(Obj,"memoryUsed",memoryUsed);
|
||||
field_to_json(Obj,"load1",load1);
|
||||
field_to_json(Obj,"load5",load5);
|
||||
field_to_json(Obj,"load15",load15);
|
||||
field_to_json(Obj,"vendors",vendors);
|
||||
field_to_json(Obj,"status",status);
|
||||
field_to_json(Obj,"deviceType",deviceType);
|
||||
field_to_json(Obj,"healths",healths);
|
||||
field_to_json(Obj,"certificates",certificates);
|
||||
field_to_json(Obj,"lastContact",lastContact);
|
||||
field_to_json(Obj,"associations",associations);
|
||||
field_to_json(Obj,"snapshot",snapshot);
|
||||
field_to_json(Obj,"numberOfDevices",numberOfDevices);
|
||||
}
|
||||
|
||||
void Dashboard::reset() {
|
||||
commands.clear();
|
||||
upTimes.clear();
|
||||
memoryUsed.clear();
|
||||
load1.clear();
|
||||
load5.clear();
|
||||
load15.clear();
|
||||
vendors.clear();
|
||||
status.clear();
|
||||
deviceType.clear();
|
||||
healths.clear();
|
||||
certificates.clear();
|
||||
lastContact.clear();
|
||||
associations.clear();
|
||||
numberOfDevices = 0 ;
|
||||
snapshot = OpenWifi::Now();
|
||||
}
|
||||
|
||||
void CapabilitiesModel::to_json(Poco::JSON::Object &Obj) const{
|
||||
field_to_json(Obj,"deviceType", deviceType);
|
||||
field_to_json(Obj,"capabilities", capabilities);
|
||||
};
|
||||
|
||||
void ScriptRequest::to_json(Poco::JSON::Object &Obj) const {
|
||||
field_to_json(Obj,"serialNumber",serialNumber);
|
||||
field_to_json(Obj,"timeout",timeout);
|
||||
field_to_json(Obj,"type",type);
|
||||
field_to_json(Obj,"script",script);
|
||||
field_to_json(Obj,"scriptId",scriptId);
|
||||
field_to_json(Obj,"when",when);
|
||||
}
|
||||
|
||||
bool ScriptRequest::from_json(const Poco::JSON::Object::Ptr &Obj) {
|
||||
try {
|
||||
field_from_json(Obj,"serialNumber",serialNumber);
|
||||
field_from_json(Obj,"timeout",timeout);
|
||||
field_from_json(Obj,"type",type);
|
||||
field_from_json(Obj,"script",script);
|
||||
field_from_json(Obj,"scriptId",scriptId);
|
||||
field_from_json(Obj,"when",when);
|
||||
return true;
|
||||
} catch (const Poco::Exception &E) {
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,213 +0,0 @@
|
||||
//
|
||||
// License type: BSD 3-Clause License
|
||||
// License copy: https://github.com/Telecominfraproject/wlan-cloud-ucentralgw/blob/master/LICENSE
|
||||
//
|
||||
// Created by Stephane Bourque on 2021-03-04.
|
||||
// Arilia Wireless Inc.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "RESTAPI_SecurityObjects.h"
|
||||
|
||||
namespace OpenWifi::GWObjects {
|
||||
|
||||
enum CertificateValidation {
|
||||
NO_CERTIFICATE,
|
||||
VALID_CERTIFICATE,
|
||||
MISMATCH_SERIAL,
|
||||
VERIFIED
|
||||
};
|
||||
|
||||
struct ConnectionState {
|
||||
uint64_t MessageCount = 0 ;
|
||||
std::string Address;
|
||||
uint64_t UUID = 0 ;
|
||||
uint64_t PendingUUID = 0 ;
|
||||
uint64_t TX = 0, RX = 0;
|
||||
uint64_t Associations_2G=0;
|
||||
uint64_t Associations_5G=0;
|
||||
bool Connected = false;
|
||||
uint64_t LastContact=0;
|
||||
std::string Firmware;
|
||||
CertificateValidation VerifiedCertificate = NO_CERTIFICATE;
|
||||
std::string Compatible;
|
||||
uint64_t kafkaClients=0;
|
||||
uint64_t webSocketClients=0;
|
||||
uint64_t kafkaPackets=0;
|
||||
uint64_t websocketPackets=0;
|
||||
std::string locale;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct Device {
|
||||
std::string SerialNumber;
|
||||
std::string DeviceType;
|
||||
std::string MACAddress;
|
||||
std::string Manufacturer;
|
||||
std::string Configuration;
|
||||
SecurityObjects::NoteInfoVec Notes;
|
||||
std::string Owner;
|
||||
std::string Location;
|
||||
std::string Firmware;
|
||||
std::string Compatible;
|
||||
std::string FWUpdatePolicy;
|
||||
uint64_t UUID = 0 ;
|
||||
uint64_t CreationTimestamp = 0 ;
|
||||
uint64_t LastConfigurationChange = 0 ;
|
||||
uint64_t LastConfigurationDownload = 0 ;
|
||||
uint64_t LastFWUpdate = 0 ;
|
||||
std::string Venue;
|
||||
std::string DevicePassword;
|
||||
std::string subscriber;
|
||||
std::string entity;
|
||||
uint64_t modified=0;
|
||||
std::string locale;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
void to_json_with_status(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
void Print() const;
|
||||
};
|
||||
|
||||
struct Statistics {
|
||||
std::string SerialNumber;
|
||||
uint64_t UUID = 0 ;
|
||||
std::string Data;
|
||||
uint64_t Recorded = 0;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct HealthCheck {
|
||||
std::string SerialNumber;
|
||||
uint64_t UUID = 0 ;
|
||||
std::string Data;
|
||||
uint64_t Recorded = 0 ;
|
||||
uint64_t Sanity = 0 ;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct Capabilities {
|
||||
std::string Capabilities;
|
||||
uint64_t FirstUpdate = 0 ;
|
||||
uint64_t LastUpdate = 0 ;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct DeviceLog {
|
||||
enum Level {
|
||||
LOG_EMERG = 0, /* system is unusable */
|
||||
LOG_ALERT = 1, /* action must be taken immediately */
|
||||
LOG_CRIT = 2, /* critical conditions */
|
||||
LOG_ERR = 3, /* error conditions */
|
||||
LOG_WARNING = 4, /* warning conditions */
|
||||
LOG_NOTICE = 5, /* normal but significant condition */
|
||||
LOG_INFO = 6, /* informational */
|
||||
LOG_DEBUG = 7 /* debug-level messages */
|
||||
};
|
||||
std::string SerialNumber;
|
||||
std::string Log;
|
||||
std::string Data;
|
||||
uint64_t Severity = 0 ;
|
||||
uint64_t Recorded = 0 ;
|
||||
uint64_t LogType = 0 ;
|
||||
uint64_t UUID = 0 ;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct DefaultConfiguration {
|
||||
std::string Name;
|
||||
std::string Configuration;
|
||||
Types::StringVec Models;
|
||||
std::string Description;
|
||||
uint64_t Created;
|
||||
uint64_t LastModified;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct CommandDetails {
|
||||
std::string UUID;
|
||||
std::string SerialNumber;
|
||||
std::string Command;
|
||||
std::string Status;
|
||||
std::string SubmittedBy;
|
||||
std::string Results;
|
||||
std::string Details;
|
||||
std::string ErrorText;
|
||||
uint64_t Submitted = time(nullptr);
|
||||
uint64_t Executed = 0;
|
||||
uint64_t Completed = 0 ;
|
||||
uint64_t RunAt = 0 ;
|
||||
uint64_t ErrorCode = 0 ;
|
||||
uint64_t Custom = 0 ;
|
||||
uint64_t WaitingForFile = 0 ;
|
||||
uint64_t AttachDate = 0 ;
|
||||
uint64_t AttachSize = 0 ;
|
||||
std::string AttachType;
|
||||
double executionTime = 0.0;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct BlackListedDevice {
|
||||
std::string serialNumber;
|
||||
std::string reason;
|
||||
std::string author;
|
||||
uint64_t created;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
|
||||
struct RttySessionDetails {
|
||||
std::string SerialNumber;
|
||||
std::string Server;
|
||||
uint64_t Port = 0 ;
|
||||
std::string Token;
|
||||
uint64_t TimeOut = 0 ;
|
||||
std::string ConnectionId;
|
||||
uint64_t Started = 0 ;
|
||||
std::string CommandUUID;
|
||||
uint64_t ViewPort = 0 ;
|
||||
std::string DevicePassword;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct Dashboard {
|
||||
uint64_t snapshot = 0 ;
|
||||
uint64_t numberOfDevices = 0 ;
|
||||
Types::CountedMap commands;
|
||||
Types::CountedMap upTimes;
|
||||
Types::CountedMap memoryUsed;
|
||||
Types::CountedMap load1;
|
||||
Types::CountedMap load5;
|
||||
Types::CountedMap load15;
|
||||
Types::CountedMap vendors;
|
||||
Types::CountedMap status;
|
||||
Types::CountedMap deviceType;
|
||||
Types::CountedMap healths;
|
||||
Types::CountedMap certificates;
|
||||
Types::CountedMap lastContact;
|
||||
Types::CountedMap associations;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
void reset();
|
||||
};
|
||||
|
||||
struct CapabilitiesModel {
|
||||
std::string deviceType;
|
||||
std::string capabilities;
|
||||
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
};
|
||||
|
||||
struct ScriptRequest {
|
||||
uint64_t timeout=30;
|
||||
std::string serialNumber;
|
||||
std::string type;
|
||||
std::string script;
|
||||
std::string scriptId;
|
||||
uint64_t when=0;
|
||||
void to_json(Poco::JSON::Object &Obj) const;
|
||||
bool from_json(const Poco::JSON::Object::Ptr &Obj);
|
||||
};
|
||||
}
|
||||
75
src/framework/RESTAPI_GenericServerAccounting.h
Normal file
75
src/framework/RESTAPI_GenericServerAccounting.h
Normal file
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <array>
|
||||
|
||||
#include "Poco/StringTokenizer.h"
|
||||
#include "Poco/String.h"
|
||||
#include "Poco/Net/HTTPRequest.h"
|
||||
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class RESTAPI_GenericServerAccounting {
|
||||
public:
|
||||
|
||||
enum {
|
||||
LOG_GET=0,
|
||||
LOG_DELETE,
|
||||
LOG_PUT,
|
||||
LOG_POST
|
||||
};
|
||||
|
||||
void inline SetFlags(bool External, const std::string &Methods) {
|
||||
Poco::StringTokenizer Tokens(Methods,",");
|
||||
auto Offset = (External ? 0 : 4);
|
||||
for(const auto &i:Tokens) {
|
||||
if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_DELETE)==0)
|
||||
LogFlags_[Offset+LOG_DELETE]=true;
|
||||
else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_PUT)==0)
|
||||
LogFlags_[Offset+LOG_PUT]=true;
|
||||
else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_POST)==0)
|
||||
LogFlags_[Offset+LOG_POST]=true;
|
||||
else if(Poco::icompare(i,Poco::Net::HTTPRequest::HTTP_GET)==0)
|
||||
LogFlags_[Offset+LOG_GET]=true;
|
||||
}
|
||||
}
|
||||
|
||||
inline void InitLogging() {
|
||||
std::string Public = MicroServiceConfigGetString("apilogging.public.methods","PUT,POST,DELETE");
|
||||
SetFlags(true, Public);
|
||||
std::string Private = MicroServiceConfigGetString("apilogging.private.methods","PUT,POST,DELETE");
|
||||
SetFlags(false, Private);
|
||||
|
||||
std::string PublicBadTokens = MicroServiceConfigGetString("apilogging.public.badtokens.methods","");
|
||||
LogBadTokens_[0] = (Poco::icompare(PublicBadTokens,"true")==0);
|
||||
std::string PrivateBadTokens = MicroServiceConfigGetString("apilogging.private.badtokens.methods","");
|
||||
LogBadTokens_[1] = (Poco::icompare(PrivateBadTokens,"true")==0);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool LogIt(const std::string &Method, bool External) const {
|
||||
auto Offset = (External ? 0 : 4);
|
||||
if(Method == Poco::Net::HTTPRequest::HTTP_GET)
|
||||
return LogFlags_[Offset+LOG_GET];
|
||||
if(Method == Poco::Net::HTTPRequest::HTTP_POST)
|
||||
return LogFlags_[Offset+LOG_POST];
|
||||
if(Method == Poco::Net::HTTPRequest::HTTP_PUT)
|
||||
return LogFlags_[Offset+LOG_PUT];
|
||||
if(Method == Poco::Net::HTTPRequest::HTTP_DELETE)
|
||||
return LogFlags_[Offset+LOG_DELETE];
|
||||
return false;
|
||||
};
|
||||
|
||||
[[nodiscard]] inline bool LogBadTokens(bool External) const {
|
||||
return LogBadTokens_[ (External ? 0 : 1) ];
|
||||
};
|
||||
|
||||
private:
|
||||
std::array<bool,8> LogFlags_{false};
|
||||
std::array<bool,2> LogBadTokens_{false};
|
||||
};
|
||||
}
|
||||
8
src/framework/RESTAPI_Handler.cpp
Normal file
8
src/framework/RESTAPI_Handler.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#include "RESTAPI_Handler.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
} // namespace OpenWifi
|
||||
826
src/framework/RESTAPI_Handler.h
Normal file
826
src/framework/RESTAPI_Handler.h
Normal file
@@ -0,0 +1,826 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include "Poco/Net/HTTPRequestHandler.h"
|
||||
#include "Poco/Logger.h"
|
||||
#include "Poco/JSON/Object.h"
|
||||
#include "Poco/JSON/Parser.h"
|
||||
#include "Poco/Net/HTTPResponse.h"
|
||||
#include "Poco/Net/HTTPServerResponse.h"
|
||||
#include "Poco/DeflatingStream.h"
|
||||
#include "Poco/TemporaryFile.h"
|
||||
#include "Poco/Net/OAuth20Credentials.h"
|
||||
|
||||
#include "framework/ow_constants.h"
|
||||
#include "framework/RESTAPI_GenericServerAccounting.h"
|
||||
#include "framework/RESTAPI_RateLimiter.h"
|
||||
#include "framework/utils.h"
|
||||
#include "framework/RESTAPI_utils.h"
|
||||
#include "framework/AuthClient.h"
|
||||
#include "RESTObjects/RESTAPI_SecurityObjects.h"
|
||||
|
||||
#if defined(TIP_SECURITY_SERVICE)
|
||||
#include "AuthService.h"
|
||||
#endif
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class RESTAPIHandler : public Poco::Net::HTTPRequestHandler {
|
||||
public:
|
||||
struct QueryBlock {
|
||||
uint64_t StartDate = 0 , EndDate = 0 , Offset = 0 , Limit = 0, LogType = 0 ;
|
||||
std::string SerialNumber, Filter;
|
||||
std::vector<std::string> Select;
|
||||
bool Lifetime=false, LastOnly=false, Newest=false, CountOnly=false, AdditionalInfo=false;
|
||||
};
|
||||
typedef std::map<std::string, std::string> BindingMap;
|
||||
|
||||
struct RateLimit {
|
||||
int64_t Interval=1000;
|
||||
int64_t MaxCalls=10;
|
||||
};
|
||||
|
||||
RESTAPIHandler( BindingMap map,
|
||||
Poco::Logger &l,
|
||||
std::vector<std::string> Methods,
|
||||
RESTAPI_GenericServerAccounting & Server,
|
||||
uint64_t TransactionId,
|
||||
bool Internal,
|
||||
bool AlwaysAuthorize=true,
|
||||
bool RateLimited=false,
|
||||
const RateLimit & Profile = RateLimit{.Interval=1000,.MaxCalls=100},
|
||||
bool SubscriberOnly=false)
|
||||
: Bindings_(std::move(map)),
|
||||
Logger_(l),
|
||||
Methods_(std::move(Methods)),
|
||||
Internal_(Internal),
|
||||
RateLimited_(RateLimited),
|
||||
SubOnlyService_(SubscriberOnly),
|
||||
AlwaysAuthorize_(AlwaysAuthorize),
|
||||
Server_(Server),
|
||||
MyRates_(Profile),
|
||||
TransactionId_(TransactionId)
|
||||
{
|
||||
}
|
||||
|
||||
inline bool RoleIsAuthorized([[maybe_unused]] const std::string & Path, [[maybe_unused]] const std::string & Method, [[maybe_unused]] std::string & Reason) {
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void handleRequest(Poco::Net::HTTPServerRequest &RequestIn,
|
||||
Poco::Net::HTTPServerResponse &ResponseIn) final {
|
||||
try {
|
||||
Request = &RequestIn;
|
||||
Response = &ResponseIn;
|
||||
|
||||
// std::string th_name = "restsvr_" + std::to_string(TransactionId_);
|
||||
// Utils::SetThreadName(th_name.c_str());
|
||||
|
||||
if(Request->getContentLength()>0) {
|
||||
if(Request->getContentType().find("application/json")!=std::string::npos) {
|
||||
ParsedBody_ = IncomingParser_.parse(Request->stream()).extract<Poco::JSON::Object::Ptr>();
|
||||
}
|
||||
}
|
||||
|
||||
if(RateLimited_ && RESTAPI_RateLimiter()->IsRateLimited(RequestIn,MyRates_.Interval, MyRates_.MaxCalls)) {
|
||||
return UnAuthorized(RESTAPI::Errors::RATE_LIMIT_EXCEEDED);
|
||||
}
|
||||
|
||||
if (!ContinueProcessing())
|
||||
return;
|
||||
|
||||
bool Expired=false, Contacted=false;
|
||||
if (AlwaysAuthorize_ && !IsAuthorized(Expired, Contacted, SubOnlyService_)) {
|
||||
if(Expired)
|
||||
return UnAuthorized(RESTAPI::Errors::EXPIRED_TOKEN);
|
||||
if(Contacted)
|
||||
return UnAuthorized(RESTAPI::Errors::INVALID_TOKEN);
|
||||
else
|
||||
return UnAuthorized(RESTAPI::Errors::SECURITY_SERVICE_UNREACHABLE);
|
||||
}
|
||||
|
||||
std::string Reason;
|
||||
if(!RoleIsAuthorized(RequestIn.getURI(), Request->getMethod(), Reason)) {
|
||||
return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
ParseParameters();
|
||||
if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_GET)
|
||||
return DoGet();
|
||||
else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_POST)
|
||||
return DoPost();
|
||||
else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_DELETE)
|
||||
return DoDelete();
|
||||
else if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_PUT)
|
||||
return DoPut();
|
||||
return BadRequest(RESTAPI::Errors::UnsupportedHTTPMethod);
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger_.log(E);
|
||||
return BadRequest(RESTAPI::Errors::InternalError);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool NeedAdditionalInfo() const { return QB_.AdditionalInfo; }
|
||||
[[nodiscard]] inline const std::vector<std::string> & SelectedRecords() const { return QB_.Select; }
|
||||
|
||||
inline static bool ParseBindings(const std::string & Request, const std::list<std::string> & EndPoints, BindingMap &bindings) {
|
||||
bindings.clear();
|
||||
auto PathItems = Poco::StringTokenizer(Request, "/");
|
||||
|
||||
for(const auto &EndPoint:EndPoints) {
|
||||
auto ParamItems = Poco::StringTokenizer(EndPoint, "/");
|
||||
if (PathItems.count() != ParamItems.count())
|
||||
continue;
|
||||
|
||||
bool Matched = true;
|
||||
for (size_t i = 0; i < PathItems.count(); i++) {
|
||||
if (PathItems[i] != ParamItems[i]) {
|
||||
if (ParamItems[i][0] == '{') {
|
||||
auto ParamName = ParamItems[i].substr(1, ParamItems[i].size() - 2);
|
||||
bindings[Poco::toLower(ParamName)] = PathItems[i];
|
||||
} else {
|
||||
Matched = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(Matched)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void PrintBindings() {
|
||||
for (const auto &[key, value] : Bindings_)
|
||||
std::cout << "Key = " << key << " Value= " << value << std::endl;
|
||||
}
|
||||
|
||||
inline void ParseParameters() {
|
||||
Poco::URI uri(Request->getURI());
|
||||
Parameters_ = uri.getQueryParameters();
|
||||
InitQueryBlock();
|
||||
}
|
||||
|
||||
inline static bool is_number(const std::string &s) {
|
||||
return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit);
|
||||
}
|
||||
|
||||
inline static bool is_bool(const std::string &s) {
|
||||
if (s == "true" || s == "false")
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline uint64_t GetParameter(const std::string &Name, const uint64_t Default) {
|
||||
auto Hint = std::find_if(Parameters_.begin(),Parameters_.end(),[&](const std::pair<std::string,std::string> &S){ return S.first==Name; });
|
||||
if(Hint==Parameters_.end() || !is_number(Hint->second))
|
||||
return Default;
|
||||
return std::stoull(Hint->second);
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool GetBoolParameter(const std::string &Name, bool Default=false) {
|
||||
auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair<std::string,std::string> &S){ return S.first==Name; });
|
||||
if(Hint==end(Parameters_) || !is_bool(Hint->second))
|
||||
return Default;
|
||||
return Hint->second=="true";
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::string GetParameter(const std::string &Name, const std::string &Default="") {
|
||||
auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair<std::string,std::string> &S){ return S.first==Name; });
|
||||
if(Hint==end(Parameters_))
|
||||
return Default;
|
||||
return Hint->second;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool HasParameter(const std::string &Name, std::string &Value) {
|
||||
auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair<std::string,std::string> &S){ return S.first==Name; });
|
||||
if(Hint==end(Parameters_))
|
||||
return false;
|
||||
Value = Hint->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool HasParameter(const std::string &Name, uint64_t & Value) {
|
||||
auto Hint = std::find_if(begin(Parameters_),end(Parameters_),[&](const std::pair<std::string,std::string> &S){ return S.first==Name; });
|
||||
if(Hint==end(Parameters_))
|
||||
return false;
|
||||
Value = std::stoull(Hint->second);
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const std::string & GetBinding(const std::string &Name, const std::string &Default="") {
|
||||
auto E = Bindings_.find(Poco::toLower(Name));
|
||||
if (E == Bindings_.end())
|
||||
return Default;
|
||||
|
||||
return E->second;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline static std::string MakeList(const std::vector<std::string> &L) {
|
||||
std::string Return;
|
||||
for (const auto &i : L) {
|
||||
if (Return.empty())
|
||||
Return = i;
|
||||
else
|
||||
Return += ", " + i;
|
||||
}
|
||||
return Return;
|
||||
}
|
||||
|
||||
static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, Types::UUIDvec_t & Value) {
|
||||
if(O->has(Field) && O->isArray(Field)) {
|
||||
auto Arr = O->getArray(Field);
|
||||
for(const auto &i:*Arr)
|
||||
Value.emplace_back(i.toString());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, std::string &Value) {
|
||||
if(O->has(Field)) {
|
||||
Value = O->get(Field).toString();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, uint64_t &Value) {
|
||||
if(O->has(Field)) {
|
||||
Value = O->get(Field);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, bool &Value) {
|
||||
if(O->has(Field)) {
|
||||
Value = O->get(Field).toString()=="true";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, double &Value) {
|
||||
if(O->has(Field)) {
|
||||
Value = (double) O->get(Field);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, Poco::Data::BLOB &Value) {
|
||||
if(O->has(Field)) {
|
||||
std::string Content = O->get(Field).toString();
|
||||
auto DecodedBlob = Utils::base64decode(Content);
|
||||
Value.assignRaw((const unsigned char *)&DecodedBlob[0],DecodedBlob.size());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template <typename T> bool AssignIfPresent(const Poco::JSON::Object::Ptr &O, const std::string &Field, const T &value, T & assignee) {
|
||||
if(O->has(Field)) {
|
||||
assignee = value;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void SetCommonHeaders(bool CloseConnection=false) {
|
||||
Response->setVersion(Poco::Net::HTTPMessage::HTTP_1_1);
|
||||
Response->setChunkedTransferEncoding(true);
|
||||
Response->setContentType("application/json");
|
||||
auto Origin = Request->find("Origin");
|
||||
if (Origin != Request->end()) {
|
||||
Response->set("Access-Control-Allow-Origin", Origin->second);
|
||||
} else {
|
||||
Response->set("Access-Control-Allow-Origin", "*");
|
||||
}
|
||||
Response->set("Vary", "Origin, Accept-Encoding");
|
||||
if(CloseConnection) {
|
||||
Response->set("Connection", "close");
|
||||
Response->setKeepAlive(false);
|
||||
} else {
|
||||
Response->setKeepAlive(true);
|
||||
Response->set("Connection", "Keep-Alive");
|
||||
Response->set("Keep-Alive", "timeout=30, max=1000");
|
||||
}
|
||||
}
|
||||
|
||||
inline void ProcessOptions() {
|
||||
Response->setVersion(Poco::Net::HTTPMessage::HTTP_1_1);
|
||||
Response->setChunkedTransferEncoding(true);
|
||||
auto Origin = Request->find("Origin");
|
||||
if (Origin != Request->end()) {
|
||||
Response->set("Access-Control-Allow-Origin", Origin->second);
|
||||
} else {
|
||||
Response->set("Access-Control-Allow-Origin", "*");
|
||||
}
|
||||
Response->set("Access-Control-Allow-Methods", MakeList(Methods_));
|
||||
auto RequestHeaders = Request->find("Access-Control-Request-Headers");
|
||||
if(RequestHeaders!=Request->end())
|
||||
Response->set("Access-Control-Allow-Headers", RequestHeaders->second);
|
||||
Response->set("Vary", "Origin, Accept-Encoding");
|
||||
Response->set("Access-Control-Allow-Credentials", "true");
|
||||
Response->set("Access-Control-Max-Age", "86400");
|
||||
Response->set("Connection", "Keep-Alive");
|
||||
Response->set("Keep-Alive", "timeout=30, max=1000");
|
||||
|
||||
Response->setContentLength(0);
|
||||
Response->setStatus(Poco::Net::HTTPResponse::HTTP_OK);
|
||||
Response->send();
|
||||
}
|
||||
|
||||
inline void PrepareResponse(Poco::Net::HTTPResponse::HTTPStatus Status = Poco::Net::HTTPResponse::HTTP_OK,
|
||||
bool CloseConnection = false) {
|
||||
Response->setStatus(Status);
|
||||
SetCommonHeaders(CloseConnection);
|
||||
}
|
||||
|
||||
inline void BadRequest(const OpenWifi::RESTAPI::Errors::msg &E, const std::string & Extra="") {
|
||||
PrepareResponse(Poco::Net::HTTPResponse::HTTP_BAD_REQUEST);
|
||||
Poco::JSON::Object ErrorObject;
|
||||
ErrorObject.set("ErrorCode",400);
|
||||
ErrorObject.set("ErrorDetails",Request->getMethod());
|
||||
if(Extra.empty())
|
||||
ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ;
|
||||
else
|
||||
ErrorObject.set("ErrorDescription",fmt::format("{}: {} ({})",E.err_num,E.err_txt, Extra)) ;
|
||||
|
||||
std::ostream &Answer = Response->send();
|
||||
Poco::JSON::Stringifier::stringify(ErrorObject, Answer);
|
||||
}
|
||||
|
||||
inline void InternalError(const OpenWifi::RESTAPI::Errors::msg &E) {
|
||||
PrepareResponse(Poco::Net::HTTPResponse::HTTP_INTERNAL_SERVER_ERROR);
|
||||
Poco::JSON::Object ErrorObject;
|
||||
ErrorObject.set("ErrorCode",500);
|
||||
ErrorObject.set("ErrorDetails",Request->getMethod());
|
||||
ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ;
|
||||
std::ostream &Answer = Response->send();
|
||||
Poco::JSON::Stringifier::stringify(ErrorObject, Answer);
|
||||
}
|
||||
|
||||
inline void UnAuthorized(const OpenWifi::RESTAPI::Errors::msg &E) {
|
||||
PrepareResponse(Poco::Net::HTTPResponse::HTTP_FORBIDDEN);
|
||||
Poco::JSON::Object ErrorObject;
|
||||
ErrorObject.set("ErrorCode",E.err_num);
|
||||
ErrorObject.set("ErrorDetails",Request->getMethod());
|
||||
ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ;
|
||||
std::ostream &Answer = Response->send();
|
||||
Poco::JSON::Stringifier::stringify(ErrorObject, Answer);
|
||||
}
|
||||
|
||||
inline void NotFound() {
|
||||
PrepareResponse(Poco::Net::HTTPResponse::HTTP_NOT_FOUND);
|
||||
Poco::JSON::Object ErrorObject;
|
||||
ErrorObject.set("ErrorCode",404);
|
||||
ErrorObject.set("ErrorDetails",Request->getMethod());
|
||||
const auto & E = OpenWifi::RESTAPI::Errors::Error404;
|
||||
ErrorObject.set("ErrorDescription",fmt::format("{}: {}",E.err_num,E.err_txt)) ;
|
||||
std::ostream &Answer = Response->send();
|
||||
Poco::JSON::Stringifier::stringify(ErrorObject, Answer);
|
||||
poco_debug(Logger_,fmt::format("RES-NOTFOUND: User='{}@{}' Method='{}' Path='{}",
|
||||
Requester(),
|
||||
Utils::FormatIPv6(Request->clientAddress().toString()),
|
||||
Request->getMethod(),
|
||||
Request->getURI()));
|
||||
}
|
||||
|
||||
inline void OK() {
|
||||
PrepareResponse();
|
||||
if( Request->getMethod()==Poco::Net::HTTPRequest::HTTP_DELETE ||
|
||||
Request->getMethod()==Poco::Net::HTTPRequest::HTTP_OPTIONS) {
|
||||
Response->send();
|
||||
} else {
|
||||
Poco::JSON::Object ErrorObject;
|
||||
ErrorObject.set("Code", 0);
|
||||
ErrorObject.set("Operation", Request->getMethod());
|
||||
ErrorObject.set("Details", "Command completed.");
|
||||
std::ostream &Answer = Response->send();
|
||||
Poco::JSON::Stringifier::stringify(ErrorObject, Answer);
|
||||
}
|
||||
}
|
||||
|
||||
inline void SendCompressedTarFile(const std::string & FileName, const std::string & Content) {
|
||||
Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK);
|
||||
SetCommonHeaders();
|
||||
Response->set("Content-Type","application/gzip");
|
||||
Response->set("Content-Disposition", "attachment; filename=" + FileName );
|
||||
Response->set("Content-Transfer-Encoding","binary");
|
||||
Response->set("Accept-Ranges", "bytes");
|
||||
Response->set("Cache-Control", "no-store");
|
||||
Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT");
|
||||
Response->setStatus(Poco::Net::HTTPResponse::HTTP_OK);
|
||||
Response->setContentLength(Content.size());
|
||||
Response->setChunkedTransferEncoding(true);
|
||||
std::ostream& OutputStream = Response->send();
|
||||
OutputStream << Content;
|
||||
}
|
||||
|
||||
inline void SendFile(Poco::File & File, const std::string & UUID) {
|
||||
Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK);
|
||||
SetCommonHeaders();
|
||||
Response->set("Content-Type","application/octet-stream");
|
||||
Response->set("Content-Disposition", "attachment; filename=" + UUID );
|
||||
Response->set("Content-Transfer-Encoding","binary");
|
||||
Response->set("Accept-Ranges", "bytes");
|
||||
Response->set("Cache-Control", "no-store");
|
||||
Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT");
|
||||
Response->setContentLength(File.getSize());
|
||||
Response->sendFile(File.path(),"application/octet-stream");
|
||||
}
|
||||
|
||||
inline void SendFile(Poco::File & File) {
|
||||
Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK);
|
||||
SetCommonHeaders();
|
||||
Poco::Path P(File.path());
|
||||
auto MT = Utils::FindMediaType(File);
|
||||
if(MT.Encoding==Utils::BINARY) {
|
||||
Response->set("Content-Transfer-Encoding","binary");
|
||||
Response->set("Accept-Ranges", "bytes");
|
||||
}
|
||||
Response->set("Cache-Control", "no-store");
|
||||
Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT");
|
||||
Response->sendFile(File.path(),MT.ContentType);
|
||||
}
|
||||
|
||||
inline void SendFile(Poco::TemporaryFile &TempAvatar, [[maybe_unused]] const std::string &Type, const std::string & Name) {
|
||||
Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK);
|
||||
SetCommonHeaders();
|
||||
auto MT = Utils::FindMediaType(Name);
|
||||
if(MT.Encoding==Utils::BINARY) {
|
||||
Response->set("Content-Transfer-Encoding","binary");
|
||||
Response->set("Accept-Ranges", "bytes");
|
||||
}
|
||||
Response->set("Content-Disposition", "attachment; filename=" + Name );
|
||||
Response->set("Accept-Ranges", "bytes");
|
||||
Response->set("Cache-Control", "no-store");
|
||||
Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT");
|
||||
Response->setContentLength(TempAvatar.getSize());
|
||||
Response->sendFile(TempAvatar.path(),MT.ContentType);
|
||||
}
|
||||
|
||||
inline void SendFileContent(const std::string &Content, const std::string &Type, const std::string & Name) {
|
||||
Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK);
|
||||
SetCommonHeaders();
|
||||
auto MT = Utils::FindMediaType(Name);
|
||||
if(MT.Encoding==Utils::BINARY) {
|
||||
Response->set("Content-Transfer-Encoding","binary");
|
||||
Response->set("Accept-Ranges", "bytes");
|
||||
}
|
||||
Response->set("Content-Disposition", "attachment; filename=" + Name );
|
||||
Response->set("Accept-Ranges", "bytes");
|
||||
Response->set("Cache-Control", "no-store");
|
||||
Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT");
|
||||
Response->setContentLength(Content.size());
|
||||
Response->setContentType(Type );
|
||||
auto & OutputStream = Response->send();
|
||||
OutputStream << Content ;
|
||||
}
|
||||
|
||||
inline void SendHTMLFileBack(Poco::File & File,
|
||||
const Types::StringPairVec & FormVars) {
|
||||
Response->setStatus(Poco::Net::HTTPResponse::HTTPStatus::HTTP_OK);
|
||||
SetCommonHeaders();
|
||||
Response->set("Pragma", "private");
|
||||
Response->set("Expires", "Mon, 26 Jul 2027 05:00:00 GMT");
|
||||
std::string FormContent = Utils::LoadFile(File.path());
|
||||
Utils::ReplaceVariables(FormContent, FormVars);
|
||||
Response->setContentLength(FormContent.size());
|
||||
Response->setChunkedTransferEncoding(true);
|
||||
Response->setContentType("text/html");
|
||||
std::ostream& ostr = Response->send();
|
||||
ostr << FormContent;
|
||||
}
|
||||
|
||||
inline void ReturnStatus(Poco::Net::HTTPResponse::HTTPStatus Status, bool CloseConnection=false) {
|
||||
PrepareResponse(Status, CloseConnection);
|
||||
if(Status == Poco::Net::HTTPResponse::HTTP_NO_CONTENT) {
|
||||
Response->setContentLength(0);
|
||||
Response->erase("Content-Type");
|
||||
Response->setChunkedTransferEncoding(false);
|
||||
}
|
||||
Response->send();
|
||||
}
|
||||
|
||||
inline bool ContinueProcessing() {
|
||||
if (Request->getMethod() == Poco::Net::HTTPRequest::HTTP_OPTIONS) {
|
||||
ProcessOptions();
|
||||
return false;
|
||||
} else if (std::find(Methods_.begin(), Methods_.end(), Request->getMethod()) == Methods_.end()) {
|
||||
BadRequest(RESTAPI::Errors::UnsupportedHTTPMethod);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool IsAuthorized(bool & Expired, bool & Contacted, bool SubOnly = false );
|
||||
|
||||
inline void ReturnObject(Poco::JSON::Object &Object) {
|
||||
PrepareResponse();
|
||||
if(Request!= nullptr) {
|
||||
// can we compress ???
|
||||
auto AcceptedEncoding = Request->find("Accept-Encoding");
|
||||
if(AcceptedEncoding!=Request->end()) {
|
||||
if( AcceptedEncoding->second.find("gzip")!=std::string::npos ||
|
||||
AcceptedEncoding->second.find("compress")!=std::string::npos) {
|
||||
Response->set("Content-Encoding", "gzip");
|
||||
std::ostream &Answer = Response->send();
|
||||
Poco::DeflatingOutputStream deflater(Answer, Poco::DeflatingStreamBuf::STREAM_GZIP);
|
||||
Poco::JSON::Stringifier::stringify(Object, deflater);
|
||||
deflater.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::ostream &Answer = Response->send();
|
||||
Poco::JSON::Stringifier::stringify(Object, Answer);
|
||||
}
|
||||
|
||||
inline void ReturnRawJSON(const std::string &json_doc) {
|
||||
PrepareResponse();
|
||||
if(Request!= nullptr) {
|
||||
// can we compress ???
|
||||
auto AcceptedEncoding = Request->find("Accept-Encoding");
|
||||
if(AcceptedEncoding!=Request->end()) {
|
||||
if( AcceptedEncoding->second.find("gzip")!=std::string::npos ||
|
||||
AcceptedEncoding->second.find("compress")!=std::string::npos) {
|
||||
Response->set("Content-Encoding", "gzip");
|
||||
std::ostream &Answer = Response->send();
|
||||
Poco::DeflatingOutputStream deflater(Answer, Poco::DeflatingStreamBuf::STREAM_GZIP);
|
||||
deflater << json_doc;
|
||||
deflater.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::ostream &Answer = Response->send();
|
||||
Answer << json_doc;
|
||||
}
|
||||
|
||||
inline void ReturnCountOnly(uint64_t Count) {
|
||||
Poco::JSON::Object Answer;
|
||||
Answer.set("count", Count);
|
||||
ReturnObject(Answer);
|
||||
}
|
||||
|
||||
inline bool InitQueryBlock() {
|
||||
if(QueryBlockInitialized_)
|
||||
return true;
|
||||
QueryBlockInitialized_=true;
|
||||
QB_.SerialNumber = GetParameter(RESTAPI::Protocol::SERIALNUMBER, "");
|
||||
QB_.StartDate = GetParameter(RESTAPI::Protocol::STARTDATE, 0);
|
||||
QB_.EndDate = GetParameter(RESTAPI::Protocol::ENDDATE, 0);
|
||||
QB_.Offset = GetParameter(RESTAPI::Protocol::OFFSET, 0);
|
||||
QB_.Limit = GetParameter(RESTAPI::Protocol::LIMIT, 100);
|
||||
QB_.Filter = GetParameter(RESTAPI::Protocol::FILTER, "");
|
||||
QB_.Lifetime = GetBoolParameter(RESTAPI::Protocol::LIFETIME,false);
|
||||
QB_.LogType = GetParameter(RESTAPI::Protocol::LOGTYPE,0);
|
||||
QB_.LastOnly = GetBoolParameter(RESTAPI::Protocol::LASTONLY,false);
|
||||
QB_.Newest = GetBoolParameter(RESTAPI::Protocol::NEWEST,false);
|
||||
QB_.CountOnly = GetBoolParameter(RESTAPI::Protocol::COUNTONLY,false);
|
||||
QB_.AdditionalInfo = GetBoolParameter(RESTAPI::Protocol::WITHEXTENDEDINFO,false);
|
||||
|
||||
auto RawSelect = GetParameter(RESTAPI::Protocol::SELECT, "");
|
||||
|
||||
auto Entries = Poco::StringTokenizer(RawSelect,",");
|
||||
for(const auto &i:Entries) {
|
||||
QB_.Select.emplace_back(i);
|
||||
}
|
||||
if(QB_.Offset<1)
|
||||
QB_.Offset=0;
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline uint64_t Get(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, uint64_t Default=0){
|
||||
if(Obj->has(Parameter))
|
||||
return Obj->get(Parameter);
|
||||
return Default;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline std::string GetS(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, const std::string & Default=""){
|
||||
if(Obj->has(Parameter))
|
||||
return Obj->get(Parameter).toString();
|
||||
return Default;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool GetB(const char *Parameter,const Poco::JSON::Object::Ptr &Obj, bool Default=false){
|
||||
if(Obj->has(Parameter))
|
||||
return Obj->get(Parameter).toString()=="true";
|
||||
return Default;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline uint64_t GetWhen(const Poco::JSON::Object::Ptr &Obj) {
|
||||
return RESTAPIHandler::Get(RESTAPI::Protocol::WHEN, Obj);
|
||||
}
|
||||
|
||||
template<typename T> void ReturnObject(const char *Name, const std::vector<T> & Objects) {
|
||||
Poco::JSON::Object Answer;
|
||||
RESTAPI_utils::field_to_json(Answer,Name,Objects);
|
||||
ReturnObject(Answer);
|
||||
}
|
||||
|
||||
template<typename T> void Object(const char *Name, const std::vector<T> & Objects) {
|
||||
Poco::JSON::Object Answer;
|
||||
RESTAPI_utils::field_to_json(Answer,Name,Objects);
|
||||
ReturnObject(Answer);
|
||||
}
|
||||
|
||||
template <typename T> void Object(const T &O) {
|
||||
Poco::JSON::Object Answer;
|
||||
O.to_json(Answer);
|
||||
ReturnObject(Answer);
|
||||
}
|
||||
|
||||
Poco::Logger & Logger() { return Logger_; }
|
||||
|
||||
virtual void DoGet() = 0 ;
|
||||
virtual void DoDelete() = 0 ;
|
||||
virtual void DoPost() = 0 ;
|
||||
virtual void DoPut() = 0 ;
|
||||
|
||||
Poco::Net::HTTPServerRequest *Request= nullptr;
|
||||
Poco::Net::HTTPServerResponse *Response= nullptr;
|
||||
SecurityObjects::UserInfoAndPolicy UserInfo_;
|
||||
QueryBlock QB_;
|
||||
const std::string & Requester() const { return REST_Requester_; }
|
||||
protected:
|
||||
BindingMap Bindings_;
|
||||
Poco::URI::QueryParameters Parameters_;
|
||||
Poco::Logger &Logger_;
|
||||
std::string SessionToken_;
|
||||
std::vector<std::string> Methods_;
|
||||
bool Internal_=false;
|
||||
bool RateLimited_=false;
|
||||
bool QueryBlockInitialized_=false;
|
||||
bool SubOnlyService_=false;
|
||||
bool AlwaysAuthorize_=true;
|
||||
Poco::JSON::Parser IncomingParser_;
|
||||
RESTAPI_GenericServerAccounting & Server_;
|
||||
RateLimit MyRates_;
|
||||
uint64_t TransactionId_;
|
||||
Poco::JSON::Object::Ptr ParsedBody_;
|
||||
std::string REST_Requester_;
|
||||
};
|
||||
|
||||
#ifdef TIP_SECURITY_SERVICE
|
||||
[[nodiscard]] bool AuthServiceIsAuthorized(Poco::Net::HTTPServerRequest & Request,std::string &SessionToken,
|
||||
SecurityObjects::UserInfoAndPolicy & UInfo, std::uint64_t TID, bool & Expired , bool Sub );
|
||||
#endif
|
||||
inline bool RESTAPIHandler::IsAuthorized( bool & Expired , [[maybe_unused]] bool & Contacted , bool Sub ) {
|
||||
if(Internal_ && Request->has("X-INTERNAL-NAME")) {
|
||||
auto Allowed = MicroServiceIsValidAPIKEY(*Request);
|
||||
Contacted = true;
|
||||
if(!Allowed) {
|
||||
if(Server_.LogBadTokens(false)) {
|
||||
poco_debug(Logger_,fmt::format("I-REQ-DENIED({}): TID={} Method={} Path={}",
|
||||
Utils::FormatIPv6(Request->clientAddress().toString()),
|
||||
TransactionId_,
|
||||
Request->getMethod(), Request->getURI()));
|
||||
}
|
||||
} else {
|
||||
auto Id = Request->get("X-INTERNAL-NAME", "unknown");
|
||||
REST_Requester_ = Id;
|
||||
if(Server_.LogIt(Request->getMethod(),true)) {
|
||||
poco_debug(Logger_,fmt::format("I-REQ-ALLOWED({}): TID={} User='{}' Method={} Path={}",
|
||||
Utils::FormatIPv6(Request->clientAddress().toString()),
|
||||
TransactionId_,
|
||||
Id,
|
||||
Request->getMethod(), Request->getURI()));
|
||||
}
|
||||
}
|
||||
return Allowed;
|
||||
} else if(!Internal_ && Request->has("X-API-KEY")) {
|
||||
SessionToken_ = Request->get("X-API-KEY", "");
|
||||
#ifdef TIP_SECURITY_SERVICE
|
||||
std::uint64_t expiresOn;
|
||||
if (AuthService()->IsValidApiKey(SessionToken_, UserInfo_.webtoken, UserInfo_.userinfo, Expired, expiresOn)) {
|
||||
#else
|
||||
if (AuthClient()->IsValidApiKey( SessionToken_, UserInfo_, TransactionId_, Expired, Contacted)) {
|
||||
#endif
|
||||
REST_Requester_ = UserInfo_.userinfo.email;
|
||||
if(Server_.LogIt(Request->getMethod(),true)) {
|
||||
poco_debug(Logger_,fmt::format("X-REQ-ALLOWED({}): APIKEY-ACCESS TID={} User='{}@{}' Method={} Path={}",
|
||||
UserInfo_.userinfo.email,
|
||||
TransactionId_,
|
||||
Utils::FormatIPv6(Request->clientAddress().toString()),
|
||||
Request->clientAddress().toString(),
|
||||
Request->getMethod(),
|
||||
Request->getURI()));
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if(Server_.LogBadTokens(true)) {
|
||||
poco_debug(Logger_,fmt::format("X-REQ-DENIED({}): TID={} Method={} Path={}",
|
||||
Utils::FormatIPv6(Request->clientAddress().toString()),
|
||||
TransactionId_,
|
||||
Request->getMethod(),
|
||||
Request->getURI()));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
if (SessionToken_.empty()) {
|
||||
try {
|
||||
Poco::Net::OAuth20Credentials Auth(*Request);
|
||||
if (Auth.getScheme() == "Bearer") {
|
||||
SessionToken_ = Auth.getBearerToken();
|
||||
}
|
||||
} catch (const Poco::Exception &E) {
|
||||
Logger_.log(E);
|
||||
}
|
||||
}
|
||||
#ifdef TIP_SECURITY_SERVICE
|
||||
if (AuthServiceIsAuthorized(*Request, SessionToken_, UserInfo_, TransactionId_, Expired, Sub)) {
|
||||
#else
|
||||
if (AuthClient()->IsAuthorized( SessionToken_, UserInfo_, TransactionId_, Expired, Contacted, Sub)) {
|
||||
#endif
|
||||
REST_Requester_ = UserInfo_.userinfo.email;
|
||||
if(Server_.LogIt(Request->getMethod(),true)) {
|
||||
poco_debug(Logger_,fmt::format("X-REQ-ALLOWED({}): TID={} User='{}@{}' Method={} Path={}",
|
||||
UserInfo_.userinfo.email,
|
||||
TransactionId_,
|
||||
Utils::FormatIPv6(Request->clientAddress().toString()),
|
||||
Request->clientAddress().toString(),
|
||||
Request->getMethod(),
|
||||
Request->getURI()));
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
if(Server_.LogBadTokens(true)) {
|
||||
poco_debug(Logger_,fmt::format("X-REQ-DENIED({}): TID={} Method={} Path={}",
|
||||
Utils::FormatIPv6(Request->clientAddress().toString()),
|
||||
TransactionId_,
|
||||
Request->getMethod(),
|
||||
Request->getURI()));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class RESTAPI_UnknownRequestHandler : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_UnknownRequestHandler(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal)
|
||||
: RESTAPIHandler(bindings, L, std::vector<std::string>{}, Server, TransactionId, Internal) {}
|
||||
inline void DoGet() override {};
|
||||
inline void DoPost() override {};
|
||||
inline void DoPut() override {};
|
||||
inline void DoDelete() override {};
|
||||
};
|
||||
|
||||
template<class T>
|
||||
constexpr auto test_has_PathName_method(T*)
|
||||
-> decltype( T::PathName() , std::true_type{} )
|
||||
{
|
||||
return std::true_type{};
|
||||
}
|
||||
constexpr auto test_has_PathName_method(...) -> std::false_type
|
||||
{
|
||||
return std::false_type{};
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
RESTAPIHandler * RESTAPI_Router(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings,
|
||||
Poco::Logger & Logger, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId) {
|
||||
static_assert(test_has_PathName_method((T*)nullptr), "Class must have a static PathName() method.");
|
||||
if(RESTAPIHandler::ParseBindings(RequestedPath,T::PathName(),Bindings)) {
|
||||
return new T(Bindings, Logger, Server, TransactionId, false);
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(Args) == 0) {
|
||||
return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server, TransactionId, false);
|
||||
} else {
|
||||
return RESTAPI_Router<Args...>(RequestedPath, Bindings, Logger, Server, TransactionId);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename... Args>
|
||||
RESTAPIHandler * RESTAPI_Router_I(const std::string & RequestedPath, RESTAPIHandler::BindingMap &Bindings,
|
||||
Poco::Logger & Logger, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId) {
|
||||
static_assert(test_has_PathName_method((T*)nullptr), "Class must have a static PathName() method.");
|
||||
if(RESTAPIHandler::ParseBindings(RequestedPath,T::PathName(),Bindings)) {
|
||||
return new T(Bindings, Logger, Server, TransactionId, true );
|
||||
}
|
||||
|
||||
if constexpr (sizeof...(Args) == 0) {
|
||||
return new RESTAPI_UnknownRequestHandler(Bindings,Logger, Server, TransactionId, true);
|
||||
} else {
|
||||
return RESTAPI_Router_I<Args...>(RequestedPath, Bindings, Logger, Server, TransactionId);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
17
src/framework/RESTAPI_IntServer.cpp
Normal file
17
src/framework/RESTAPI_IntServer.cpp
Normal file
@@ -0,0 +1,17 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#include "RESTAPI_IntServer.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
Poco::Net::HTTPRequestHandler *
|
||||
IntRequestHandlerFactory::createRequestHandler(const Poco::Net::HTTPServerRequest &Request) {
|
||||
auto TID = NextTransactionId_++;
|
||||
Utils::SetThreadName(fmt::format("i-rest:{}", TID).c_str());
|
||||
Poco::URI uri(Request.getURI());
|
||||
return RESTAPI_IntServer()->CallServer(uri.getPath(), TID);
|
||||
}
|
||||
|
||||
} // namespace OpenWifi
|
||||
104
src/framework/RESTAPI_IntServer.h
Normal file
104
src/framework/RESTAPI_IntServer.h
Normal file
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Poco/Net/HTTPServer.h"
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
Poco::Net::HTTPRequestHandler * RESTAPI_IntRouter(const std::string &Path, RESTAPIHandler::BindingMap &Bindings,
|
||||
Poco::Logger & L, RESTAPI_GenericServerAccounting & S, uint64_t Id);
|
||||
|
||||
class IntRequestHandlerFactory : public Poco::Net::HTTPRequestHandlerFactory {
|
||||
public:
|
||||
inline IntRequestHandlerFactory() = default;
|
||||
Poco::Net::HTTPRequestHandler *createRequestHandler(const Poco::Net::HTTPServerRequest &Request) override;
|
||||
private:
|
||||
static inline std::atomic_uint64_t NextTransactionId_ = 1;
|
||||
};
|
||||
|
||||
class RESTAPI_IntServer : public SubSystemServer {
|
||||
public:
|
||||
static auto instance() {
|
||||
static auto instance_ = new RESTAPI_IntServer;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
inline int Start() override {
|
||||
poco_information(Logger(),"Starting.");
|
||||
Server_.InitLogging();
|
||||
|
||||
for(const auto & Svr: ConfigServersList_) {
|
||||
|
||||
if(MicroServiceNoAPISecurity()) {
|
||||
poco_information(Logger(),fmt::format("Starting: {}:{}. Security has been disabled for APIs.", Svr.Address(), Svr.Port()));
|
||||
} else {
|
||||
poco_information(Logger(),fmt::format("Starting: {}:{}. Keyfile:{} CertFile: {}", Svr.Address(), Svr.Port(),
|
||||
Svr.KeyFile(),Svr.CertFile()));
|
||||
Svr.LogCert(Logger());
|
||||
if (!Svr.RootCA().empty())
|
||||
Svr.LogCas(Logger());
|
||||
}
|
||||
|
||||
auto Params = new Poco::Net::HTTPServerParams;
|
||||
Params->setKeepAlive(true);
|
||||
Params->setName("ws:irest");
|
||||
|
||||
std::unique_ptr<Poco::Net::HTTPServer> NewServer;
|
||||
if(MicroServiceNoAPISecurity()) {
|
||||
auto Sock{Svr.CreateSocket(Logger())};
|
||||
NewServer = std::make_unique<Poco::Net::HTTPServer>(new IntRequestHandlerFactory, Pool_, Sock, Params);
|
||||
} else {
|
||||
auto Sock{Svr.CreateSecureSocket(Logger())};
|
||||
NewServer = std::make_unique<Poco::Net::HTTPServer>(new IntRequestHandlerFactory, Pool_, Sock, Params);
|
||||
};
|
||||
NewServer->start();
|
||||
RESTServers_.push_back(std::move(NewServer));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void Stop() override {
|
||||
poco_information(Logger(),"Stopping...");
|
||||
for( const auto & svr : RESTServers_ )
|
||||
svr->stopAll(true);
|
||||
Pool_.stopAll();
|
||||
Pool_.joinAll();
|
||||
poco_information(Logger(),"Stopped...");
|
||||
}
|
||||
|
||||
inline void reinitialize([[maybe_unused]] Poco::Util::Application &self) override {
|
||||
MicroServiceLoadConfigurationFile();
|
||||
poco_information(Logger(),"Reinitializing.");
|
||||
Stop();
|
||||
Start();
|
||||
}
|
||||
|
||||
inline Poco::Net::HTTPRequestHandler *CallServer(const std::string &Path, uint64_t Id) {
|
||||
RESTAPIHandler::BindingMap Bindings;
|
||||
Utils::SetThreadName(fmt::format("i-rest:{}",Id).c_str());
|
||||
return RESTAPI_IntRouter(Path, Bindings, Logger(), Server_, Id);
|
||||
}
|
||||
const Poco::ThreadPool & Pool() { return Pool_; }
|
||||
private:
|
||||
std::vector<std::unique_ptr<Poco::Net::HTTPServer>> RESTServers_;
|
||||
Poco::ThreadPool Pool_{"i-rest",4,64};
|
||||
RESTAPI_GenericServerAccounting Server_;
|
||||
|
||||
RESTAPI_IntServer() noexcept:
|
||||
SubSystemServer("RESTAPI_IntServer", "REST-ISRV", "openwifi.internal.restapi")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
inline auto RESTAPI_IntServer() { return RESTAPI_IntServer::instance(); };
|
||||
|
||||
|
||||
} // namespace OpenWifi
|
||||
|
||||
66
src/framework/RESTAPI_PartHandler.h
Normal file
66
src/framework/RESTAPI_PartHandler.h
Normal file
@@ -0,0 +1,66 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-26.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include "Poco/Net/PartHandler.h"
|
||||
#include "Poco/Net/MessageHeader.h"
|
||||
#include "Poco/CountingStream.h"
|
||||
#include "Poco/NullStream.h"
|
||||
#include "Poco/StreamCopier.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
class RESTAPI_PartHandler: public Poco::Net::PartHandler {
|
||||
public:
|
||||
RESTAPI_PartHandler():
|
||||
_length(0)
|
||||
{
|
||||
}
|
||||
|
||||
inline void handlePart(const Poco::Net::MessageHeader& header, std::istream& stream) override
|
||||
{
|
||||
_type = header.get("Content-Type", "(unspecified)");
|
||||
if (header.has("Content-Disposition"))
|
||||
{
|
||||
std::string disp;
|
||||
Poco::Net::NameValueCollection params;
|
||||
Poco::Net::MessageHeader::splitParameters(header["Content-Disposition"], disp, params);
|
||||
_name = params.get("name", "(unnamed)");
|
||||
_fileName = params.get("filename", "(unnamed)");
|
||||
}
|
||||
|
||||
Poco::CountingInputStream istr(stream);
|
||||
Poco::NullOutputStream ostr;
|
||||
Poco::StreamCopier::copyStream(istr, ostr);
|
||||
_length = (int)istr.chars();
|
||||
}
|
||||
|
||||
[[nodiscard]] inline int length() const
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const std::string& name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const std::string& fileName() const
|
||||
{
|
||||
return _fileName;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline const std::string& contentType() const
|
||||
{
|
||||
return _type;
|
||||
}
|
||||
|
||||
private:
|
||||
int _length;
|
||||
std::string _type;
|
||||
std::string _name;
|
||||
std::string _fileName;
|
||||
};
|
||||
}
|
||||
75
src/framework/RESTAPI_RateLimiter.h
Normal file
75
src/framework/RESTAPI_RateLimiter.h
Normal file
@@ -0,0 +1,75 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/SubSystemServer.h"
|
||||
|
||||
#include "Poco/URI.h"
|
||||
#include "Poco/Net/HTTPServerRequest.h"
|
||||
#include "Poco/ExpireLRUCache.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class RESTAPI_RateLimiter : public SubSystemServer {
|
||||
public:
|
||||
|
||||
struct ClientCacheEntry {
|
||||
int64_t Start=0;
|
||||
int Count=0;
|
||||
};
|
||||
|
||||
static auto instance() {
|
||||
static auto instance_ = new RESTAPI_RateLimiter;
|
||||
return instance_;
|
||||
}
|
||||
|
||||
inline int Start() final { return 0;};
|
||||
inline void Stop() final { };
|
||||
|
||||
inline bool IsRateLimited(const Poco::Net::HTTPServerRequest &R, int64_t Period, int64_t MaxCalls) {
|
||||
Poco::URI uri(R.getURI());
|
||||
auto H = str_hash(uri.getPath() + R.clientAddress().host().toString());
|
||||
auto E = Cache_.get(H);
|
||||
auto Now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
if(E.isNull()) {
|
||||
Cache_.add(H,ClientCacheEntry{.Start=Now, .Count=1});
|
||||
return false;
|
||||
}
|
||||
if((Now-E->Start)<Period) {
|
||||
E->Count++;
|
||||
Cache_.update(H,E);
|
||||
if(E->Count > MaxCalls) {
|
||||
poco_warning(Logger(),fmt::format("RATE-LIMIT-EXCEEDED: from '{}'", R.clientAddress().toString()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
E->Start = Now;
|
||||
E->Count = 1;
|
||||
Cache_.update(H,E);
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void Clear() {
|
||||
Cache_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
Poco::ExpireLRUCache<uint64_t,ClientCacheEntry> Cache_{2048};
|
||||
std::hash<std::string> str_hash;
|
||||
|
||||
RESTAPI_RateLimiter() noexcept:
|
||||
SubSystemServer("RateLimiter", "RATE-LIMITER", "rate.limiter")
|
||||
{
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
inline auto RESTAPI_RateLimiter() { return RESTAPI_RateLimiter::instance(); }
|
||||
|
||||
|
||||
}
|
||||
157
src/framework/RESTAPI_SystemCommand.h
Normal file
157
src/framework/RESTAPI_SystemCommand.h
Normal file
@@ -0,0 +1,157 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-25.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
|
||||
#include "Poco/Environment.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class RESTAPI_system_command : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_system_command(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L, RESTAPI_GenericServerAccounting & Server, uint64_t TransactionId, bool Internal)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_POST,
|
||||
Poco::Net::HTTPRequest::HTTP_GET,
|
||||
Poco::Net::HTTPRequest::HTTP_OPTIONS},
|
||||
Server,
|
||||
TransactionId,
|
||||
Internal) {}
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/system"};}
|
||||
|
||||
inline void DoGet() {
|
||||
std::string Arg;
|
||||
if(HasParameter("command",Arg) && Arg=="info") {
|
||||
Poco::JSON::Object Answer;
|
||||
Answer.set(RESTAPI::Protocol::VERSION, MicroServiceVersion());
|
||||
Answer.set(RESTAPI::Protocol::UPTIME, MicroServiceUptimeTotalSeconds());
|
||||
Answer.set(RESTAPI::Protocol::START, MicroServiceStartTimeEpochTime());
|
||||
Answer.set(RESTAPI::Protocol::OS, Poco::Environment::osName());
|
||||
Answer.set(RESTAPI::Protocol::PROCESSORS, Poco::Environment::processorCount());
|
||||
Answer.set(RESTAPI::Protocol::HOSTNAME, Poco::Environment::nodeName());
|
||||
Answer.set(RESTAPI::Protocol::UI, MicroServiceGetUIURI());
|
||||
|
||||
Poco::JSON::Array Certificates;
|
||||
auto SubSystems = MicroServiceGetFullSubSystems();
|
||||
std::set<std::string> CertNames;
|
||||
|
||||
for(const auto &i:SubSystems) {
|
||||
auto Hosts=i->HostSize();
|
||||
for(uint64_t j=0;j<Hosts;++j) {
|
||||
auto CertFileName = i->Host(j).CertFile();
|
||||
if(!CertFileName.empty()) {
|
||||
Poco::File F1(CertFileName);
|
||||
if(F1.exists()) {
|
||||
auto InsertResult = CertNames.insert(CertFileName);
|
||||
if(InsertResult.second) {
|
||||
Poco::JSON::Object Inner;
|
||||
Poco::Path F(CertFileName);
|
||||
Inner.set("filename", F.getFileName());
|
||||
Poco::Crypto::X509Certificate C(CertFileName);
|
||||
auto ExpiresOn = C.expiresOn();
|
||||
Inner.set("expiresOn", ExpiresOn.timestamp().epochTime());
|
||||
Certificates.add(Inner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Answer.set("certificates", Certificates);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
if(GetBoolParameter("extraConfiguration")) {
|
||||
Poco::JSON::Object Answer;
|
||||
MicroServiceGetExtraConfiguration(Answer);
|
||||
return ReturnObject(Answer);
|
||||
}
|
||||
BadRequest(RESTAPI::Errors::InvalidCommand);
|
||||
}
|
||||
|
||||
inline void DoPost() final {
|
||||
const auto & Obj = ParsedBody_;
|
||||
if (Obj->has(RESTAPI::Protocol::COMMAND)) {
|
||||
auto Command = Poco::toLower(Obj->get(RESTAPI::Protocol::COMMAND).toString());
|
||||
if (Command == RESTAPI::Protocol::SETLOGLEVEL) {
|
||||
if (Obj->has(RESTAPI::Protocol::SUBSYSTEMS) &&
|
||||
Obj->isArray(RESTAPI::Protocol::SUBSYSTEMS)) {
|
||||
auto ParametersBlock = Obj->getArray(RESTAPI::Protocol::SUBSYSTEMS);
|
||||
for (const auto &i : *ParametersBlock) {
|
||||
Poco::JSON::Parser pp;
|
||||
auto InnerObj = pp.parse(i).extract<Poco::JSON::Object::Ptr>();
|
||||
if (InnerObj->has(RESTAPI::Protocol::TAG) &&
|
||||
InnerObj->has(RESTAPI::Protocol::VALUE)) {
|
||||
auto Name = GetS(RESTAPI::Protocol::TAG, InnerObj);
|
||||
auto Value = GetS(RESTAPI::Protocol::VALUE, InnerObj);
|
||||
MicroServiceSetSubsystemLogLevel(Name, Value);
|
||||
poco_information(Logger_,
|
||||
fmt::format("Setting log level for {} at {}", Name, Value));
|
||||
}
|
||||
}
|
||||
return OK();
|
||||
}
|
||||
} else if (Command == RESTAPI::Protocol::GETLOGLEVELS) {
|
||||
auto CurrentLogLevels = MicroServiceGetLogLevels();
|
||||
Poco::JSON::Object Result;
|
||||
Poco::JSON::Array Array;
|
||||
for (auto &[Name, Level] : CurrentLogLevels) {
|
||||
Poco::JSON::Object Pair;
|
||||
Pair.set(RESTAPI::Protocol::TAG, Name);
|
||||
Pair.set(RESTAPI::Protocol::VALUE, Level);
|
||||
Array.add(Pair);
|
||||
}
|
||||
Result.set(RESTAPI::Protocol::TAGLIST, Array);
|
||||
return ReturnObject(Result);
|
||||
} else if (Command == RESTAPI::Protocol::GETLOGLEVELNAMES) {
|
||||
Poco::JSON::Object Result;
|
||||
Poco::JSON::Array LevelNamesArray;
|
||||
const Types::StringVec &LevelNames = MicroServiceGetLogLevelNames();
|
||||
for (const auto &i : LevelNames)
|
||||
LevelNamesArray.add(i);
|
||||
Result.set(RESTAPI::Protocol::LIST, LevelNamesArray);
|
||||
return ReturnObject(Result);
|
||||
} else if (Command == RESTAPI::Protocol::GETSUBSYSTEMNAMES) {
|
||||
Poco::JSON::Object Result;
|
||||
Poco::JSON::Array LevelNamesArray;
|
||||
const Types::StringVec &SubSystemNames = MicroServiceGetSubSystems();
|
||||
for (const auto &i : SubSystemNames)
|
||||
LevelNamesArray.add(i);
|
||||
Result.set(RESTAPI::Protocol::LIST, LevelNamesArray);
|
||||
return ReturnObject(Result);
|
||||
} else if (Command == RESTAPI::Protocol::STATS) {
|
||||
|
||||
} else if (Command == RESTAPI::Protocol::RELOAD) {
|
||||
if (Obj->has(RESTAPI::Protocol::SUBSYSTEMS) &&
|
||||
Obj->isArray(RESTAPI::Protocol::SUBSYSTEMS)) {
|
||||
auto SubSystems = Obj->getArray(RESTAPI::Protocol::SUBSYSTEMS);
|
||||
std::vector<std::string> Names;
|
||||
for (const auto &i : *SubSystems)
|
||||
Names.push_back(i.toString());
|
||||
std::thread ReloadThread([Names](){
|
||||
std::this_thread::sleep_for(10000ms);
|
||||
for(const auto &i:Names) {
|
||||
if(i=="daemon")
|
||||
MicroServiceReload();
|
||||
else
|
||||
MicroServiceReload(i);
|
||||
}
|
||||
});
|
||||
ReloadThread.detach();
|
||||
}
|
||||
return OK();
|
||||
}
|
||||
} else {
|
||||
return BadRequest(RESTAPI::Errors::InvalidCommand);
|
||||
}
|
||||
BadRequest(RESTAPI::Errors::MissingOrInvalidParameters);
|
||||
}
|
||||
|
||||
void DoPut() final {};
|
||||
void DoDelete() final {};
|
||||
};
|
||||
|
||||
}
|
||||
52
src/framework/RESTAPI_SystemConfiguration.h
Normal file
52
src/framework/RESTAPI_SystemConfiguration.h
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// Created by stephane bourque on 2022-10-31.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "framework/RESTAPI_Handler.h"
|
||||
#include "framework/MicroServiceFuncs.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace OpenWifi {
|
||||
|
||||
class RESTAPI_system_configuration : public RESTAPIHandler {
|
||||
public:
|
||||
RESTAPI_system_configuration(const RESTAPIHandler::BindingMap &bindings, Poco::Logger &L,
|
||||
RESTAPI_GenericServerAccounting &Server, uint64_t TransactionId,
|
||||
bool Internal)
|
||||
: RESTAPIHandler(bindings, L,
|
||||
std::vector<std::string>{Poco::Net::HTTPRequest::HTTP_PUT,
|
||||
Poco::Net::HTTPRequest::HTTP_GET,
|
||||
Poco::Net::HTTPRequest::HTTP_DELETE,
|
||||
Poco::Net::HTTPRequest::HTTP_OPTIONS},
|
||||
Server, TransactionId, Internal) {}
|
||||
|
||||
static auto PathName() { return std::list<std::string>{"/api/v1/systemConfiguration"}; }
|
||||
|
||||
inline void DoPost() final {}
|
||||
|
||||
inline void DoGet() final {
|
||||
|
||||
return OK();
|
||||
}
|
||||
|
||||
inline void DoPut() final{
|
||||
if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) {
|
||||
return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
|
||||
}
|
||||
|
||||
return OK();
|
||||
};
|
||||
|
||||
inline void DoDelete() final{
|
||||
if(UserInfo_.userinfo.userRole!=SecurityObjects::ROOT) {
|
||||
return UnAuthorized(RESTAPI::Errors::ACCESS_DENIED);
|
||||
}
|
||||
MicroServiceDeleteOverrideConfiguration();
|
||||
return OK();
|
||||
};
|
||||
};
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user