diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6557406 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.git +.github +.idea +vendor \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4798b3a..06c748e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ img/bg.* Gemfile.lock -composer.lock +_api/composer.lock vendor/ .bundle _site diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f750a93 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,67 @@ +FROM alpine + +### +# Jekyll +### + +# Install Jekyll dependencies +RUN apk add --no-cache build-base libxml2-dev libxslt-dev ruby-full ruby-dev gcc linux-headers + +# Copy Jekyll files +RUN adduser -D -u 1000 -h /tmp/www www +COPY --chown=www:www jekyll /tmp/jekyll +WORKDIR /tmp/jekyll + +USER www + +# Install gems +RUN bundle config build.nokogiri --use-system-libraries +RUN bundle config set path 'vendor/bundle' +RUN bundle install + +# Build site +RUN bundle exec jekyll build + +USER root +RUN mv _site /opt/www/ + +# Remove Jekyll dependencies +RUN apk del build-base libxml2-dev libxslt-dev ruby-full ruby-dev gcc linux-headers + +### +# PHP +### + +# Install PHP dependencies +RUN apk add --no-cache php-fpm php-curl php-json php-session composer + +# Set up PHP-FPM +COPY php-install.sh / +RUN chmod +x /php-install.sh; /bin/sh /php-install.sh; rm /php-install.sh + +# Copy PHP files +COPY ./php /opt/php +WORKDIR /opt/php + +# Install libraries +RUN composer install --no-dev + +RUN apk del composer + +### +# Nginx +### + +# Install Nginx +RUN apk add --no-cache nginx + +# Copy Nginx virtual server config +COPY ./nginx.conf /etc/nginx/conf.d/default.conf + +# Bug fixes +RUN mkdir /run/nginx +RUN sed -i 's/cgi.fix_pathinfo= 0/cgi.fix_pathinfo=1/g' /etc/php7/php.ini +ONBUILD RUN chown www:www /opt/www/ -R + +EXPOSE 80 +CMD php-fpm7 && nginx; tail -F /var/log/nginx/error.log \ No newline at end of file diff --git a/Gemfile b/Gemfile deleted file mode 100644 index bd8098a..0000000 --- a/Gemfile +++ /dev/null @@ -1,6 +0,0 @@ -source 'https://rubygems.org' - - -group :jekyll_plugins do - gem 'github-pages' -end diff --git a/README.md b/README.md index 4785009..8463cab 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,28 @@ -# Unify Guest Portal +# UniFi Guest Portal + +## Usage + +```BASH +docker run -p 80:80 carlgo11/guest-portal +``` + +Optionally, add some background images: + +```BASH +docker run -p 80:80 -v $(pwd)/bg.webp:/opt/www/img/bg.webp -v $(pwd)/bg.jpg:/opt/www/img/bg.jpg carlgo11/guest-portal +``` + +## Environment variables + +|Name|Default|Description|Example| +|----|-------|-----------|-------| +|UNIFI_USER| |UniFi Hotspot username|api +|UNIFI_PASSWORD| |UniFi Hotspot password|password +|UNIFI_URL| |UniFi Controller IP/URL & port|https://192.168.1.2:8443 +|UNIFI_SITE|default|UniFi Site|default +|UNIFI_VERSION| |Controller version|5.13.32 ## License This work is licensed under the Creative Commons Attribution 4.0 International License. -To view a copy of this license, visit [LICENSE](LICENSE). \ No newline at end of file +To view a copy of this license, visit [LICENSE](LICENSE). diff --git a/_api/submit.php b/_api/submit.php deleted file mode 100644 index de15d74..0000000 --- a/_api/submit.php +++ /dev/null @@ -1,44 +0,0 @@ - "Invalid mac address", 'success' => FALSE], JSON_PRETTY_PRINT)); - return; -} - -$unifi_connection = new UniFi_API\Client($_ENV['hotspot_user'], $_ENV['hotspot_password'], $_ENV['unifi_url'], $_ENV['unifi_site'], $_ENV['unifi_version'], FALSE); -$login = $unifi_connection->login(); -$vouchers = $unifi_connection->stat_voucher(); - -if (isset($code)) { - foreach ($vouchers as $voucher) { - $voucher = get_object_vars($voucher); - if ($voucher['code'] == $code) { - $max_up = NULL; - $max_down = NULL; - $usage_quota = NULL; - if (isset($voucher['qos_rate_max_up'])) $max_up = $voucher['qos_rate_max_up']; - if (isset($voucher['qos_rate_max_down'])) $max_down = $voucher['qos_rate_max_down']; - if (isset($voucher['qos_usage_quota'])) $usage_quota = $voucher['qos_usage_quota']; - - $authorized = $unifi_connection->authorize_guest($mac, $voucher['duration'], $max_up, $max_down, $usage_quota, $ap_mac); - $unifi_connection->revoke_voucher($voucher['_id']); - header('Status: 202'); - print(json_encode(['success' => TRUE], JSON_PRETTY_PRINT)); - return; - } - } -} - -header('Status: 400'); -print(json_encode(['success' => FALSE, 'error' => 'Voucher code not found'], JSON_PRETTY_PRINT)); -return; diff --git a/jekyll/Gemfile b/jekyll/Gemfile new file mode 100644 index 0000000..ab38815 --- /dev/null +++ b/jekyll/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gem 'jekyll' \ No newline at end of file diff --git a/_config.yml b/jekyll/_config.yml similarity index 94% rename from _config.yml rename to jekyll/_config.yml index 98a4cc4..9be6f8f 100644 --- a/_config.yml +++ b/jekyll/_config.yml @@ -1,9 +1,9 @@ -theme: null -exclude: - - vendor/bundle - - vendor/cache - - .bundle - - .gitignore - - composer.json - - Gemfile +theme: null +exclude: + - vendor/bundle + - vendor/cache + - .bundle + - .gitignore + - composer.json + - Gemfile - README.md \ No newline at end of file diff --git a/_includes/approval.html b/jekyll/_includes/approval.html similarity index 97% rename from _includes/approval.html rename to jekyll/_includes/approval.html index 4abe313..9caf239 100644 --- a/_includes/approval.html +++ b/jekyll/_includes/approval.html @@ -1,11 +1,11 @@ - -
-
-
- Loading... -
-
-
Back
-

Waiting for - approval...

+ +
+
+
+ Loading... +
+
+
Back
+

Waiting for + approval...

\ No newline at end of file diff --git a/_includes/error.html b/jekyll/_includes/error.html similarity index 97% rename from _includes/error.html rename to jekyll/_includes/error.html index ac6dffe..0c4a3d8 100644 --- a/_includes/error.html +++ b/jekyll/_includes/error.html @@ -1,8 +1,8 @@ - -
-
Back
-
-

-

Error:

-
+ +
+
Back
+
+

+

Error:

+
\ No newline at end of file diff --git a/_includes/form.html b/jekyll/_includes/form.html similarity index 92% rename from _includes/form.html rename to jekyll/_includes/form.html index a959e67..304347f 100644 --- a/_includes/form.html +++ b/jekyll/_includes/form.html @@ -1,19 +1,19 @@ - -
-
-

Guest Wifi

-
- - - - - -
- -
or
- -
+ +
+
+

Guest Wifi

+
+ + + + + +
+ +
or
+ +
\ No newline at end of file diff --git a/_includes/loading.html b/jekyll/_includes/loading.html similarity index 96% rename from _includes/loading.html rename to jekyll/_includes/loading.html index 2b7524d..08a5692 100644 --- a/_includes/loading.html +++ b/jekyll/_includes/loading.html @@ -1,9 +1,9 @@ - -
-

Communicating with network...

-
-
- Loading... -
-
+ +
+

Communicating with network...

+
+
+ Loading... +
+
\ No newline at end of file diff --git a/_includes/success.html b/jekyll/_includes/success.html similarity index 96% rename from _includes/success.html rename to jekyll/_includes/success.html index f470259..81c9abd 100644 --- a/_includes/success.html +++ b/jekyll/_includes/success.html @@ -1,4 +1,4 @@ - -
-

Welcome! 😄

+ +
+

Welcome! 😄

\ No newline at end of file diff --git a/_layouts/default.html b/jekyll/_layouts/default.html similarity index 97% rename from _layouts/default.html rename to jekyll/_layouts/default.html index fd36e5c..faadd71 100644 --- a/_layouts/default.html +++ b/jekyll/_layouts/default.html @@ -1,26 +1,26 @@ - - - - - - Guest WiFi - - - - - - - - - -
- {{ content }} -
- - - - {% if page.name == 'index.html' %}{% endif %} - {% if page.name == 'admin.html' %}{% endif %} - - + + + + + + Guest WiFi + + + + + + + + + +
+ {{ content }} +
+ + + + {% if page.name == 'index.html' %}{% endif %} + {% if page.name == 'admin.html' %}{% endif %} + + \ No newline at end of file diff --git a/admin.html b/jekyll/admin.html similarity index 100% rename from admin.html rename to jekyll/admin.html diff --git a/css/all.min.css b/jekyll/css/all.min.css similarity index 100% rename from css/all.min.css rename to jekyll/css/all.min.css diff --git a/css/bootstrap.min.css b/jekyll/css/bootstrap.min.css similarity index 100% rename from css/bootstrap.min.css rename to jekyll/css/bootstrap.min.css diff --git a/css/main.css b/jekyll/css/main.css similarity index 93% rename from css/main.css rename to jekyll/css/main.css index 0e3ccf7..9355e22 100644 --- a/css/main.css +++ b/jekyll/css/main.css @@ -1,212 +1,214 @@ -@font-face { - font-family: 'Roboto'; - font-weight: 100; - font-style: normal; - font-display: swap; - src: local('Roboto Thin'), url('/fonts/Roboto-Thin.ttf') format('truetype'); -} - -@font-face { - font-family: 'Roboto'; - src: local('Roboto Light'), url('/fonts/Roboto-Light.ttf') format('truetype'); - font-weight: 300; - font-style: normal; - font-display: swap; -} - -.webp body { - background: url("/img/bg.webp") no-repeat; - background-size: cover; - background-position: center center; - background-attachment: fixed; - -} - -.no-webp body { - background: url("/img/bg.jpg") no-repeat; - background-size: cover; - background-position: center center; - background-attachment: fixed; -} - -body { - font-family: 'SF Pro Text', -apple-system, system-ui, 'Raleway', sans-serif; -} - -h1, -h2, -h3, -h4 { - font-family: 'SF Pro Display', -apple-system, system-ui, 'Raleway', sans-serif; - font-weight: 100; -} - -.title { - text-align: center; - line-height: 1.5; -} - -.main { - background-color: rgba(255, 255, 255, 0.9); - color: rgba(0, 0, 0, 0.7); - padding: 15px; - text-align: center; - box-shadow: 0 3px 8px 0 rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(0, 0, 0, 0.08); - - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - min-height: 252px; -} - -.spinner-grow { - width: 4em; - height: 4em; -} - -.form-control, -.btn { - border-radius: 0; -} - -.btn-back { - float: left; - font-size: 1.5em; - cursor: pointer; - font-weight: 300; - color: #222222b3; -} - -.separator { - margin: 25px auto; - display: flex; - align-items: center; - text-align: center; -} - -.separator::before, -.separator::after { - content: ''; - flex: 1; - border-bottom: 1px solid rgba(0, 0, 0, .1); -} - -.separator::before { - margin-right: .25em; -} - -.separator::after { - margin-left: .25em; -} - -#success h1 { - position: absolute; - top: 40%; - transform: translate(-50%, -50); - text-align: center; - width: 100%; -} - -#approval .spinner { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - margin-top: -2rem; -} - -#approval>h2 { - bottom: 0; - width: 100%; - text-align: center; - position: absolute; - margin-bottom: 3vh; -} - -#loading>div { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -#loading>div>div { - width: 3rem; - height: 3rem; -} - -#error-container { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); -} - -#error-container>h1>i { - font-size: 65px; - line-height: 100px; -} - -#error-container>#error-msg { - font-weight: 200; - width: max-content; -} - -textarea:hover, -input:hover, -textarea:active, -input:active, -textarea:focus, -input:focus, -button:focus, -button:active, -button:hover, -label:focus, -.btn:active, -.btn.active { - outline: 0px !important; - -webkit-appearance: none; - box-shadow: none !important; - border-color: #ced4da !important; -} - -#error { - display: none; -} - -#success { - display: none; -} - -#loading { - display: none; -} - -#approval { - display: none; -} - -#manual { - min-width: max-content; -} - -@media screen and (min-width: 541px) { - #manual { - padding: 6px 35px; - width: max-content !important; - } -} - -.noselect { - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} - -.request-buttons button { - width: 42px; +@font-face { + font-family: 'Roboto'; + font-weight: 100; + font-style: normal; + font-display: swap; + src: local('Roboto Thin'), url('/fonts/Roboto-Thin.ttf') format('truetype'); +} + +@font-face { + font-family: 'Roboto'; + src: local('Roboto Light'), url('/fonts/Roboto-Light.ttf') format('truetype'); + font-weight: 300; + font-style: normal; + font-display: swap; +} + +.webp body { + background: url("/img/bg.webp") no-repeat; + background-size: cover; + background-position: center center; + background-attachment: fixed; + +} + +.no-webp body { + background: url("/img/bg.jpg") no-repeat; + background-size: cover; + background-position: center center; + background-attachment: fixed; +} + +body { + font-family: 'SF Pro Text', -apple-system, system-ui, 'Raleway', sans-serif; +} + +h1, +h2, +h3, +h4 { + font-family: 'SF Pro Display', -apple-system, system-ui, 'Raleway', sans-serif; + font-weight: 100; +} + +.title { + text-align: center; + line-height: 1.5; +} + +.main { + background-color: rgba(255, 255, 255, 0.8); + backdrop-filter: blur(5px); + -webkit-backdrop-filter: blur(5px); + color: rgba(0, 0, 0, 0.7); + padding: 15px; + text-align: center; + box-shadow: 0 3px 8px 0 rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(0, 0, 0, 0.08); + + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + min-height: 252px; +} + +.spinner-grow { + width: 4em; + height: 4em; +} + +.form-control, +.btn { + border-radius: 0; +} + +.btn-back { + float: left; + font-size: 1.5em; + cursor: pointer; + font-weight: 300; + color: #222222b3; +} + +.separator { + margin: 25px auto; + display: flex; + align-items: center; + text-align: center; +} + +.separator::before, +.separator::after { + content: ''; + flex: 1; + border-bottom: 1px solid rgba(0, 0, 0, .1); +} + +.separator::before { + margin-right: .25em; +} + +.separator::after { + margin-left: .25em; +} + +#success h1 { + position: absolute; + top: 40%; + transform: translate(-50%, -50); + text-align: center; + width: 100%; +} + +#approval .spinner { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + margin-top: -2rem; +} + +#approval>h2 { + bottom: 0; + width: 100%; + text-align: center; + position: absolute; + margin-bottom: 3vh; +} + +#loading>div { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +#loading>div>div { + width: 3rem; + height: 3rem; +} + +#error-container { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +#error-container>h1>i { + font-size: 65px; + line-height: 100px; +} + +#error-container>#error-msg { + font-weight: 200; + width: max-content; +} + +textarea:hover, +input:hover, +textarea:active, +input:active, +textarea:focus, +input:focus, +button:focus, +button:active, +button:hover, +label:focus, +.btn:active, +.btn.active { + outline: 0px !important; + -webkit-appearance: none; + box-shadow: none !important; + border-color: #ced4da !important; +} + +#error { + display: none; +} + +#success { + display: none; +} + +#loading { + display: none; +} + +#approval { + display: none; +} + +#manual { + min-width: max-content; +} + +@media screen and (min-width: 541px) { + #manual { + padding: 6px 35px; + width: max-content !important; + } +} + +.noselect { + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.request-buttons button { + width: 42px; } \ No newline at end of file diff --git a/fonts/Roboto-Light.ttf b/jekyll/fonts/Roboto-Light.ttf similarity index 100% rename from fonts/Roboto-Light.ttf rename to jekyll/fonts/Roboto-Light.ttf diff --git a/fonts/Roboto-Thin.ttf b/jekyll/fonts/Roboto-Thin.ttf similarity index 100% rename from fonts/Roboto-Thin.ttf rename to jekyll/fonts/Roboto-Thin.ttf diff --git a/img/apple-touch-icon.png b/jekyll/img/apple-touch-icon.png similarity index 100% rename from img/apple-touch-icon.png rename to jekyll/img/apple-touch-icon.png diff --git a/img/favicon.png b/jekyll/img/favicon.png similarity index 100% rename from img/favicon.png rename to jekyll/img/favicon.png diff --git a/img/favicon.svg b/jekyll/img/favicon.svg similarity index 100% rename from img/favicon.svg rename to jekyll/img/favicon.svg diff --git a/index.html b/jekyll/index.html similarity index 94% rename from index.html rename to jekyll/index.html index 4c2b8a7..ac8d0a5 100644 --- a/index.html +++ b/jekyll/index.html @@ -1,14 +1,14 @@ ---- -layout: default ---- -
- {% include form.html %} - - {% include loading.html %} - - {% include approval.html %} - - {% include error.html %} - - {% include success.html %} +--- +layout: default +--- +
+ {% include form.html %} + + {% include loading.html %} + + {% include approval.html %} + + {% include error.html %} + + {% include success.html %}
\ No newline at end of file diff --git a/js/admin.js b/jekyll/js/admin.js similarity index 100% rename from js/admin.js rename to jekyll/js/admin.js diff --git a/js/jquery-ui.min.js b/jekyll/js/jquery-ui.min.js similarity index 100% rename from js/jquery-ui.min.js rename to jekyll/js/jquery-ui.min.js diff --git a/js/jquery.min.js b/jekyll/js/jquery.min.js similarity index 100% rename from js/jquery.min.js rename to jekyll/js/jquery.min.js diff --git a/js/main.js b/jekyll/js/main.js similarity index 96% rename from js/main.js rename to jekyll/js/main.js index ab65610..97c15d1 100644 --- a/js/main.js +++ b/jekyll/js/main.js @@ -1,81 +1,81 @@ -$(document).ready(function () { - if (getUrlVars()['ssid'] != null) { - let ssid = decodeURIComponent(getUrlVars()['ssid'].replace(/\+/g, ' ')); - $('.title').text(ssid + " WiFi"); - document.title = decodeURIComponent(ssid); - } -}); - -function getUrlVars() { - const vars = {}; - window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, - function (m, key, value) { - vars[key] = value; - }); - return vars; -} - -$('form').submit(function (e) { - // Prevent browser from connecting itself to action path - e.preventDefault(); - - let data = getUrlVars(); - let form = $(this); - data['code'] = form.serializeArray()[0]['value']; - $.ajax({ - type: 'POST', - url: form.attr('action'), - dataType: 'json', - data: data, - timeout: 5000, - accepts: { - text: 'application/json' - }, - success: function () { - // Display success window? - displayView('#success'); - setTimeout(function () { - window.location.replace(decodeURIComponent(getUrlVars()['url'])); - }, 2000); - }, - beforeSend: function () { - // Display #loading - displayView('#loading') - }, - error: function (res) { - // Display #error with #error-msg set - displayView('#error'); - console.error(res); - $('#error-msg').text('Error: ' + res.responseJSON['error']); - - } - - }); - - -}); - -$('#manual').click(function (e) { - displayView('#approval'); -}); - -function displayView(view) { - $('.view').hide(); - $(view).show(); -} -$('.btn-back').click(function () { - location.reload(); -}); - - -$('#otp').keyup(function () { - - var foo = $(this).val().split("-").join(""); // remove hyphens - if (foo.length > 0) { - foo = foo.match(new RegExp('.{1,5}', 'g')).join("-"); - } - $(this).val(foo); - if ($(this).val().length == 11) { - $('form').submit(); - } +$(document).ready(function () { + if (getUrlVars()['ssid'] != null) { + let ssid = decodeURIComponent(getUrlVars()['ssid'].replace(/\+/g, ' ')); + $('.title').text(ssid + " WiFi"); + document.title = decodeURIComponent(ssid); + } +}); + +function getUrlVars() { + const vars = {}; + window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, + function (m, key, value) { + vars[key] = value; + }); + return vars; +} + +$('form').submit(function (e) { + // Prevent browser from connecting itself to action path + e.preventDefault(); + + let data = getUrlVars(); + let form = $(this); + data['code'] = form.serializeArray()[0]['value']; + $.ajax({ + type: 'POST', + url: form.attr('action'), + dataType: 'json', + data: data, + timeout: 5000, + accepts: { + text: 'application/json' + }, + success: function () { + // Display success window? + displayView('#success'); + setTimeout(function () { + window.location.replace(decodeURIComponent(getUrlVars()['url'])); + }, 2000); + }, + beforeSend: function () { + // Display #loading + displayView('#loading') + }, + error: function (res) { + // Display #error with #error-msg set + displayView('#error'); + console.error(res); + $('#error-msg').text('Error: ' + res.responseJSON['error']); + + } + + }); + + +}); + +$('#manual').click(function (e) { + displayView('#approval'); +}); + +function displayView(view) { + $('.view').hide(); + $(view).show(); +} +$('.btn-back').click(function () { + location.reload(); +}); + + +$('#otp').keyup(function () { + + var foo = $(this).val().split("-").join(""); // remove hyphens + if (foo.length > 0) { + foo = foo.match(new RegExp('.{1,5}', 'g')).join("-"); + } + $(this).val(foo); + if ($(this).val().length == 11) { + $('form').submit(); + } }); \ No newline at end of file diff --git a/js/modenizr-webp.min.js b/jekyll/js/modenizr-webp.min.js similarity index 100% rename from js/modenizr-webp.min.js rename to jekyll/js/modenizr-webp.min.js diff --git a/webfonts/fa-brands-400.eot b/jekyll/webfonts/fa-brands-400.eot similarity index 100% rename from webfonts/fa-brands-400.eot rename to jekyll/webfonts/fa-brands-400.eot diff --git a/webfonts/fa-brands-400.svg b/jekyll/webfonts/fa-brands-400.svg similarity index 100% rename from webfonts/fa-brands-400.svg rename to jekyll/webfonts/fa-brands-400.svg diff --git a/webfonts/fa-brands-400.ttf b/jekyll/webfonts/fa-brands-400.ttf similarity index 100% rename from webfonts/fa-brands-400.ttf rename to jekyll/webfonts/fa-brands-400.ttf diff --git a/webfonts/fa-brands-400.woff b/jekyll/webfonts/fa-brands-400.woff similarity index 100% rename from webfonts/fa-brands-400.woff rename to jekyll/webfonts/fa-brands-400.woff diff --git a/webfonts/fa-brands-400.woff2 b/jekyll/webfonts/fa-brands-400.woff2 similarity index 100% rename from webfonts/fa-brands-400.woff2 rename to jekyll/webfonts/fa-brands-400.woff2 diff --git a/webfonts/fa-regular-400.eot b/jekyll/webfonts/fa-regular-400.eot similarity index 100% rename from webfonts/fa-regular-400.eot rename to jekyll/webfonts/fa-regular-400.eot diff --git a/webfonts/fa-regular-400.svg b/jekyll/webfonts/fa-regular-400.svg similarity index 100% rename from webfonts/fa-regular-400.svg rename to jekyll/webfonts/fa-regular-400.svg diff --git a/webfonts/fa-regular-400.ttf b/jekyll/webfonts/fa-regular-400.ttf similarity index 100% rename from webfonts/fa-regular-400.ttf rename to jekyll/webfonts/fa-regular-400.ttf diff --git a/webfonts/fa-regular-400.woff b/jekyll/webfonts/fa-regular-400.woff similarity index 100% rename from webfonts/fa-regular-400.woff rename to jekyll/webfonts/fa-regular-400.woff diff --git a/webfonts/fa-regular-400.woff2 b/jekyll/webfonts/fa-regular-400.woff2 similarity index 100% rename from webfonts/fa-regular-400.woff2 rename to jekyll/webfonts/fa-regular-400.woff2 diff --git a/webfonts/fa-solid-900.eot b/jekyll/webfonts/fa-solid-900.eot similarity index 100% rename from webfonts/fa-solid-900.eot rename to jekyll/webfonts/fa-solid-900.eot diff --git a/webfonts/fa-solid-900.svg b/jekyll/webfonts/fa-solid-900.svg similarity index 100% rename from webfonts/fa-solid-900.svg rename to jekyll/webfonts/fa-solid-900.svg diff --git a/webfonts/fa-solid-900.ttf b/jekyll/webfonts/fa-solid-900.ttf similarity index 100% rename from webfonts/fa-solid-900.ttf rename to jekyll/webfonts/fa-solid-900.ttf diff --git a/webfonts/fa-solid-900.woff b/jekyll/webfonts/fa-solid-900.woff similarity index 100% rename from webfonts/fa-solid-900.woff rename to jekyll/webfonts/fa-solid-900.woff diff --git a/webfonts/fa-solid-900.woff2 b/jekyll/webfonts/fa-solid-900.woff2 similarity index 100% rename from webfonts/fa-solid-900.woff2 rename to jekyll/webfonts/fa-solid-900.woff2 diff --git a/nginx.conf b/nginx.conf new file mode 100644 index 0000000..a65c7e2 --- /dev/null +++ b/nginx.conf @@ -0,0 +1,20 @@ +server { + server_name _; + listen 80; + index index.html; + + location / { + root /opt/www; + } + + rewrite ^/guest/s/default /; + + location ~ /api/ { + root /opt/php; + rewrite ^/api/(.*)$ /$1 break; + fastcgi_pass 127.0.0.1:9000; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include /etc/nginx/fastcgi_params; + include /etc/nginx/fastcgi.conf; + } +} \ No newline at end of file diff --git a/php-install.sh b/php-install.sh new file mode 100644 index 0000000..b5f4dfc --- /dev/null +++ b/php-install.sh @@ -0,0 +1,29 @@ +#!/bin/sh +PHP_FPM_USER="www" +PHP_FPM_GROUP="www" +PHP_FPM_LISTEN_MODE="0660" +PHP_MEMORY_LIMIT="512M" +PHP_MAX_UPLOAD="50M" +PHP_MAX_FILE_UPLOAD="200" +PHP_MAX_POST="100M" +PHP_DISPLAY_ERRORS="On" +PHP_DISPLAY_STARTUP_ERRORS="On" +PHP_ERROR_REPORTING="E_COMPILE_ERROR\|E_RECOVERABLE_ERROR\|E_ERROR\|E_CORE_ERROR" +PHP_CGI_FIX_PATHINFO=0 + +sed -i "s|;listen.owner\s*=\s*nobody|listen.owner = ${PHP_FPM_USER}|g" /etc/php7/php-fpm.d/www.conf +sed -i "s|;listen.group\s*=\s*nobody|listen.group = ${PHP_FPM_GROUP}|g" /etc/php7/php-fpm.d/www.conf +sed -i "s|;listen.mode\s*=\s*0660|listen.mode = ${PHP_FPM_LISTEN_MODE}|g" /etc/php7/php-fpm.d/www.conf +sed -i "s|user\s*=\s*nobody|user = ${PHP_FPM_USER}|g" /etc/php7/php-fpm.d/www.conf +sed -i "s|group\s*=\s*nobody|group = ${PHP_FPM_GROUP}|g" /etc/php7/php-fpm.d/www.conf +sed -i "s|;log_level\s*=\s*notice|log_level = notice|g" /etc/php7/php-fpm.d/www.conf +echo "clear_env = no" >> /etc/php7/php-fpm.d/www.conf + +sed -i "s|display_errors\s*=\s*Off|display_errors = ${PHP_DISPLAY_ERRORS}|i" /etc/php7/php.ini +sed -i "s|display_startup_errors\s*=\s*Off|display_startup_errors = ${PHP_DISPLAY_STARTUP_ERRORS}|i" /etc/php7/php.ini +sed -i "s|error_reporting\s*=\s*E_ALL & ~E_DEPRECATED & ~E_STRICT|error_reporting = ${PHP_ERROR_REPORTING}|i" /etc/php7/php.ini +sed -i "s|;*memory_limit =.*|memory_limit = ${PHP_MEMORY_LIMIT}|i" /etc/php7/php.ini +sed -i "s|;*upload_max_filesize =.*|upload_max_filesize = ${PHP_MAX_UPLOAD}|i" /etc/php7/php.ini +sed -i "s|;*max_file_uploads =.*|max_file_uploads = ${PHP_MAX_FILE_UPLOAD}|i" /etc/php7/php.ini +sed -i "s|;*post_max_size =.*|post_max_size = ${PHP_MAX_POST}|i" /etc/php7/php.ini +sed -i "s|;*cgi.fix_pathinfo=.*|cgi.fix_pathinfo= ${PHP_CGI_FIX_PATHINFO}|i" /etc/php7/php.ini diff --git a/composer.json b/php/composer.json similarity index 94% rename from composer.json rename to php/composer.json index 329f2cc..3929924 100644 --- a/composer.json +++ b/php/composer.json @@ -1,5 +1,5 @@ -{ - "require": { - "art-of-wifi/unifi-api-client": "^1.1" - } +{ + "require": { + "art-of-wifi/unifi-api-client": "^1.1" + } } \ No newline at end of file diff --git a/php/composer.lock b/php/composer.lock new file mode 100644 index 0000000..489353d --- /dev/null +++ b/php/composer.lock @@ -0,0 +1,66 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "a293dddafbd825921f8f8c42e10de571", + "packages": [ + { + "name": "art-of-wifi/unifi-api-client", + "version": "v1.1.56", + "source": { + "type": "git", + "url": "https://github.com/Art-of-WiFi/UniFi-API-client.git", + "reference": "0d99d4a776eb9c0cb523fc8bad9ec93084125a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Art-of-WiFi/UniFi-API-client/zipball/0d99d4a776eb9c0cb523fc8bad9ec93084125a1d", + "reference": "0d99d4a776eb9c0cb523fc8bad9ec93084125a1d", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "UniFi_API\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Art of WiFi", + "email": "info@artofowifi.net", + "homepage": "http://artofwifi.net" + } + ], + "description": "API client class for use with Ubiquiti's UniFi controller", + "homepage": "https://github.com/Art-of-WiFi/UniFi-API-client", + "keywords": [ + "UBNT", + "api", + "client", + "controller", + "ubiquiti", + "unifi" + ], + "time": "2020-06-13T10:28:43+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [], + "plugin-api-version": "1.1.0" +} diff --git a/_api/guests.php b/php/guests.php similarity index 89% rename from _api/guests.php rename to php/guests.php index 5cfe909..35d93ba 100644 --- a/_api/guests.php +++ b/php/guests.php @@ -1,5 +1,5 @@ "Invalid mac address.", 'success' => FALSE], JSON_PRETTY_PRINT)); + return; +} + +$unifi_connection = new UniFi_API\Client(getenv('UNIFI_USER'), getenv('UNIFI_PASSWORD'), getenv('UNIFI_URL'), getenv('UNIFI_SITE'), getenv('UNIFI_VERSION'), false); +$login = $unifi_connection->login(); +if (!$login) { + header('Status: 403'); + print(json_encode(['error' => "Couldn't authenticate to server.", 'success' => FALSE], JSON_PRETTY_PRINT)); +} +$vouchers = $unifi_connection->stat_voucher(); + + +if (!isset($code)) { + header('Status: 400'); + print(json_encode(['error' => "No voucher code received.", 'success' => FALSE], JSON_PRETTY_PRINT)); +} + +foreach ($vouchers as $voucher) { + $voucher = get_object_vars($voucher); + if ($voucher['code'] == $code) { + $max_up = NULL; + $max_down = NULL; + $usage_quota = NULL; + if (isset($voucher['qos_rate_max_up'])) $max_up = $voucher['qos_rate_max_up']; + if (isset($voucher['qos_rate_max_down'])) $max_down = $voucher['qos_rate_max_down']; + if (isset($voucher['qos_usage_quota'])) $usage_quota = $voucher['qos_usage_quota']; + + $authorized = $unifi_connection->authorize_guest($mac, $voucher['duration'], $max_up, $max_down, $usage_quota, $ap_mac); + $unifi_connection->revoke_voucher($voucher['_id']); + header('Status: 202'); + print(json_encode(['success' => TRUE], JSON_PRETTY_PRINT)); + return; + } +} + +header('Status: 400'); +print(json_encode(['success' => FALSE, 'error' => 'Voucher code incorrect.'], JSON_PRETTY_PRINT)); + return;