The first web front end of uBeac, built between late 2017 and 2018. It is two things in one codebase: an operator console for the platform's device catalog, teams, users and ingestion endpoints, and a UX lab where the dashboard, indoor floor-plan and map ideas that later shipped in the customer app were first prototyped.
Status: historical, published for reference. This app was superseded by the admin section of ui.ubeac.io and the operator controllers of api.ubeac.io. The admin API it was written against is not part of the public release, and its sign-in used an OAuth client and account routes that the released identity server doesn't have. With the public code, only the prototypes run, on built-in mock data. It is published under the MIT license. Read Known limitations and security notes before running it.
| Repository | What it is |
|---|---|
| api.ubeac.io | .NET backend: ingestion hubs, processing workers, REST API, identity, real-time. Start there for the platform architecture and the comparison with other IoT platforms. |
| ui.ubeac.io | Vue customer web app, which absorbed this panel's admin features |
| admin.ubeac.io (this repo) | The first operator console and UX prototypes |
| OSMonitoring | Edge agent: a computer's own sensors (CPU, memory, disks, network, temperatures) as a uBeac device |
| SBCGateway | Edge agent: a Raspberry Pi as a BLE, Bluetooth and Wi-Fi scanning gateway |
- At a glance
- Where it fits in uBeac's history
- Features: working and prototype
- Tech stack
- Architecture
- The generic CRUD pattern
- Authentication
- Domain model
- The prototypes
- Routes and backend endpoints
- Project structure
- Getting started
- Known limitations and security notes
- License
| Built | About November 2017 to mid-2018 (design files are dated October 2017 to February 2018) |
| Framework | Vue 2.4, Vuex 3, vue-router 2.7, webpack 3 (vue-cli webpack template) |
| UI | Bootstrap 4 (beta) and bootstrap-vue 1.4, Highcharts 6, custom SVG gauges |
| Size | 58 Vue components and about 11,000 lines of hand-written Vue, JavaScript and SCSS |
| Working features | Catalog, team, endpoint and user management; endpoint message-log viewer |
| Prototypes | Sensor-card dashboard, indoor floor-plan editor, geo map with drill-down |
flowchart LR
subgraph Y2017["2017 to 2018"]
ADM["admin.ubeac.io<br/>operator console and prototypes"]
AAPI["Admin API<br/>adminapi.ubeac.io"]
ING1["Ingestion and log service<br/>on Azure App Service"]
end
subgraph Y2018["2018 to 2020"]
UI["ui.ubeac.io<br/>customer web app"]
API["api.ubeac.io<br/>microservice backend"]
end
ADM -- "catalog admin moved to<br/>the web app's admin section" --> UI
ADM -- "dashboard, floor plan and map<br/>ideas shipped as widgets" --> UI
AAPI -- "replaced by uBeac.Api<br/>admin controllers" --> API
ING1 -- "replaced by HttpHub, MqttHub<br/>and the worker pipeline" --> API
What carried forward:
- The device catalog. The panel's Manufacturer → Gateway → Gateway Version chain became Manufacturer → Product → Firmware. The "processor code for this firmware version" field became the per-firmware decoder that the backend compiles at runtime, the core of uBeac's gateway support.
- Indoor floor plans. The drag-and-drop floor-plan editor became the Floor widget and the floor-placement maps in the customer app.
- Map drill-down. The geo map that zooms from a building footprint into its floor plan became the Buildings, Floors and Map features.
- Sensor cards. The temperature, gas and light cards with SVG gauges became the Indicator widget and its library of animated indicators.
- Conventions. The
[controller]/[action]routes, the{ data }response envelope, theFile/UploadandFile/Download/{id}endpoints and the IdentityServer login flow all continued in the later apps.
| Feature | Status | Backend |
|---|---|---|
| Sign in, sign up, forgot and reset password, sign out | Worked against the original identity server (not the released one) | Identity server |
| Manufacturers, gateway models and gateway versions (firmware) | Working CRUD with logo and document uploads | Admin API |
| Teams and ingestion endpoints | Working CRUD; each endpoint shows its ingestion URL (the endpoint pages and the message log show different host names for it) | Admin API |
| Users | Working create and update with roles (Users, Admins) and admin password reset |
Admin API |
| IoT Hub message log | Working: paged messages per endpoint, JSON viewer, auto-refresh (3, 5, 10 or 15 seconds, or a custom interval) | Ingestion service |
| Sensor-card dashboard | Prototype: 12 card types, drag to reorder | Mock data |
| Floor-plan editor | Prototype: drag sensors onto a plan, pan and zoom, "3D" view | Mock data |
| Geo map | Prototype: satellite and street layers, shape drawing, drill-down into the floor plan | Demo shapes |
| Purpose | Libraries |
|---|---|
| Framework | Vue 2.4.2, Vuex 3.0, vue-router 2.7 (history mode) |
| Build | webpack 3.6, Babel 6, vue-loader 13, ESLint (standard) and stylelint inside webpack, favicons-webpack-plugin |
| UI | Bootstrap 4.0 beta, bootstrap-vue 1.4, vue-awesome icons, Simple Line Icons |
| HTTP | vue-resource 1.3 with interceptors; raw XMLHttpRequest for the identity server |
| Forms | vee-validate 2.0, vue-picture-input, vue-datepicker |
| Charts | Highcharts 6 (Highstock) with vue-highcharts; hand-built SVG gauges |
| Maps | Leaflet 1.3 with Leaflet.Editable, OpenStreetMap, Bing and Google layers |
| Floor plan | jQuery UI drag and drop, jquery.panzoom, vuedraggable |
| Feedback | vue-notification, NProgress, vue-spinner, vue-breadcrumbs |
| Tests | Karma, Mocha, Sinon-Chai, PhantomJS |
flowchart LR
subgraph Browser["Browser: admin.ubeac.io"]
PAGES["Pages and cards"]
PROTO["Floor-plan editor and geo map"]
STORE["Vuex store"]
HTTP["vue-resource<br/>Bearer interceptor, 401 redirect"]
MOCK[("src/mock")]
PAGES --> STORE --> HTTP
PAGES --> MOCK
PROTO --> MOCK
end
IDS["Identity server<br/>idsrv.ubeac.io"]
AAPI["Admin API<br/>adminapi.ubeac.io"]
ING["Ingestion and log service<br/>Azure App Service"]
DEV["Gateways and devices"]
STORE -- "XHR: token, user info" --> IDS
HTTP -- "catalog, teams, endpoints,<br/>users, files" --> AAPI
HTTP -- "endpoint message log" --> ING
DEV -- "JSON POST to /api/i/{shortUrl}" --> ING
flowchart TB
MAIN["main.js<br/>plugins, interceptors, global components"] --> APP["App.vue<br/>progress bar, notifications, router-view"]
MAIN --> ROUTER["router<br/>page title and token guard"]
ROUTER --> PAGES
subgraph PAGES["components/pages"]
AUTH["auth/*"]
CATALOG["manufacturer, gateway, gateway_version"]
TENANCY["endpoint, myendpoint, team, user"]
HUB["iothub (message log)"]
PROTOS["Dashboard, Map, Leaf (prototypes)"]
end
PAGES --> PANEL["layout/Panel<br/>header, sidebar, breadcrumbs"]
CATALOG --> MIXINS["mixins/page-grid.js, page-form.js"]
CATALOG & TENANCY --> PARTIALS["partial/*<br/>grid header, empty state, delete button,<br/>file uploader, date pickers"]
PROTOS --> CARDS["cards/*<br/>3 sensors x 4 variants, SVG gauges"]
MIXINS --> STORE["store/modules/*<br/>base.js CRUD plus entity modules"]
TENANCY --> STORE
HUB --> STORE
Most catalog pages are built from three reusable pieces: a Vuex base module that speaks the API's CRUD convention, a list-page mixin and a form-page mixin. A new entity needs only a store module that sets its API path and grid columns, plus two small page components.
classDiagram
direction LR
class BaseModule {
base.js
state: list, item, path
all() GET path/getall
getItem(id) GET path/GetById/id
add(item) PUT path/Add
update(item) POST path/Update
remove(id) DELETE path/Remove
}
class ManufacturerModule {
path = manufacturer
gridFields
}
class GatewayModule {
path = Gateway
gridFields
}
class GatewayVersionModule {
path = GatewayFirmware
gridFields
}
class PageGrid {
mixin page-grid.js
loads the list on mount
grid or table view
}
class PageForm {
mixin page-form.js
add or update by route id
validation and file upload
}
BaseModule <|-- ManufacturerModule
BaseModule <|-- GatewayModule
BaseModule <|-- GatewayVersionModule
PageGrid ..> BaseModule : dispatch all
PageForm ..> BaseModule : dispatch getItem, add, update
List pages switch between a card grid and a table (the table is hidden on phones), show an empty state, and link each row to its edit page. Edit pages show audit fields (created and updated, by whom) and counts of related records.
sequenceDiagram
autonumber
participant U as Operator
participant SPA as Admin panel
participant IDS as Identity server
participant API as Admin API
U->>SPA: email and password
SPA->>IDS: POST connect/token (password grant, client AdminUIClientRO, scope AdminApi and others)
IDS-->>SPA: access token, saved in localStorage
SPA->>IDS: POST connect/userinfo and api/user/claims
SPA->>API: requests with Authorization: Bearer token
API-->>SPA: 401 when the token has expired
SPA->>SPA: redirect to /login?redirecturl=...
The route guard only checks that a token exists. Roles are fetched but not used in the UI, so the API is the only gate for operator-only actions.
The panel uses uBeac's early vocabulary. Each term maps to a later concept in the platform.
erDiagram
MANUFACTURER ||--o{ GATEWAY : makes
GATEWAY ||--o{ GATEWAYVERSION : "has versions"
GATEWAYVERSION ||--o{ ENDPOINT : "used by"
TEAM ||--o{ ENDPOINT : owns
ENDPOINT ||--o{ LOGMESSAGE : receives
| This panel | Later in uBeac | Notes |
|---|---|---|
| Manufacturer | Manufacturer | Name, website, logo |
| Gateway | Product | A hardware model, with manual and specification files |
Gateway Version (GatewayFirmware) |
Firmware | Release date and "processor code", which became the runtime-compiled decoder |
| Endpoint | Gateway | A team's ingestion URL. Here it is a short code (/api/i/{shortUrl}); later it became {namespace}.hub.ubeac.io/{gatewayUrl} |
| Team | Team | The tenant |
| Log message | GatewayData | A raw request received by an endpoint |
Twelve card components (src/components/cards/) cover three sensor types, temperature, gas and light, in four variants each:
| Variant | Shows |
|---|---|
| Basic | A custom SVG gauge (thermometer, gas bar or sun) plus the value, optional humidity and a sparkline |
| Chart | A titled Highcharts chart of recent history |
| Group | A row of sensors, each with a gauge or sparkline |
| Log | A table of readings with up, down and flat change indicators |
Cards take data and settings props, and settings (chart type, theme, details) are deep-merged over defaults. The dashboard lets you reorder cards by dragging in edit mode. The card designs follow a spec in graphic/carts.fw.png, and src/mock/cards.js documents the payload each card expects.
src/components/partial/Map/Map.vue (route /map) is an interactive editor for placing sensors on a building floor plan:
- Drag sensors from a palette onto an SVG floor plan. Drop positions are stored as percentages, corrected for the current zoom.
- Pan and zoom with the mouse wheel or buttons; pins resize with the zoom level.
- Hover a pin to remove it, move it or open a detail view with a large gauge.
- Switch floors, search pins and adjust pin and map scale.
- An "experimental 3D view" tilts the plan into an isometric perspective with CSS transforms.
src/components/pages/Leaf.vue (route /geo) shows a Leaflet map with OpenStreetMap, Bing and Google satellite layers. Users can draw markers, lines, polygons, rectangles and circles. Clicking a building footprint zooms in and fades into the floor-plan editor (the same sample plan for every shape).
flowchart LR
WORLD["Geo map<br/>OpenStreetMap, Bing, Google"] -- "click a building footprint" --> ZOOM["Zoom to the shape"]
ZOOM -- "fade in" --> PLAN["Floor-plan editor<br/>for that building"]
PLAN -- "select a floor" --> PINS["Sensor pins with gauges<br/>(mock values)"]
| Area | Paths |
|---|---|
| Authentication | /login, /signup, /forgot_password, /reset_password, /change_password, /logout, /profile |
| Catalog | /manufacturers, /manufacturers/:id, /gateways, /gateways/:id, /gatewayversion, /gatewayversion/:id |
| Tenancy | /team, /team/:id, /endpoint, /endpoint/:id, /myendpoint, /myendpoint/:id, /users, /users/:id, /users/changepassword/:id |
| Message log | /iothub/:id |
| Prototypes | / (dashboard), /map (floor plan), /geo (geo map) |
| Other | /setting, /kitchensink, * (not found) |
:id is an entity ID, or new to create one.
The admin API and the log service below belong to uBeac's first backend, which is not part of the public release.
| Service | Endpoints |
|---|---|
| Admin API | {entity}/getall, {entity}/GetById/{id}, {entity}/Add (PUT), {entity}/Update (POST), {entity}/Remove (DELETE) for manufacturer, Gateway, GatewayFirmware, Endpoint and Team; Endpoint/GetWithDependencies, Gateway/GetbyManufacturerId/{id}, GatewayFirmware/GetbyGatewayId/{id}; api/admin/* for users; File/Upload, File/Download/{id} |
| Log service | GET /i/{shortUrl}?pageNumber&rowCount&fromDate&toDate |
| Identity server | connect/token, connect/userinfo, api/user/claims, api/user/register, api/user/forgotpassword, api/user/resetpassword, api/user/logout |
admin.ubeac.io/
├── build/ webpack 3 configuration and dev server
├── config/ Environment settings (dev server on port 60001)
├── graphic/ Design sources: logo, card designs, dashboard mockups (Illustrator, Fireworks)
├── static/ Vendored libraries (panzoom, Leaflet.Editable, Prism), icons, sample floor plans
├── src/
│ ├── main.js Plugins, HTTP interceptors, global components
│ ├── config.js API base URL, validation and progress-bar settings
│ ├── router/ Routes and the auth guard
│ ├── store/modules/ base.js CRUD, entity modules, auth, iothub, layout
│ ├── components/
│ │ ├── pages/ One folder per feature
│ │ ├── cards/ Sensor cards, SVG gauges, chart configs
│ │ ├── partial/ Shared UI, including the floor-plan editor (Map/)
│ │ └── layout/ Page shell
│ ├── mixins/ page-grid.js and page-form.js
│ ├── mock/ Mock data for the prototypes and payload conventions
│ └── sass/ Global, page, component and card styles
├── test/ Karma unit test set-up
└── index.html Loads Leaflet, jQuery UI and map providers from CDNs
nvm use 8 # node-sass 4.7 and webpack 3 need an older Node.js; 8 LTS was current then
npm install
npm run dev # http://localhost:60001
npm run build # production build to dist/- The prototypes (
/,/map,/geo) run on mock data. The route guard requires a token, so set any value fortokenin the browser'slocalStorageto open them without signing in. - The admin API base URL is in
src/config.jsand the identity server URL insrc/store/modules/auth.js, but host names are also hard-coded inFileUploader.vue, the endpoint pages,store/modules/iothub.jsandIothub.vue. The data pages need the original admin API, which is not included. - The Bing and Google layers on the geo map need your own keys (
src/components/pages/Leaf.vueand the Google Maps script inindex.html). - There is no lockfile, so
npm installresolves the version ranges fresh; expect to pin a few packages.
This is an early prototype, published for its design history.
- Secrets. The OAuth client secret and the Bing Maps key were replaced with placeholders before publication. Treat any value from an earlier copy as compromised.
- OAuth flow. The panel uses the resource-owner password grant with a client secret in the browser, keeps tokens in
localStorage, and doesn't revoke them on logout. - Plain HTTP. API and identity URLs use
http://. - Unpinned CDN scripts.
index.htmlloads several scripts without version pins or integrity hashes, some over plain HTTP or from hosts that no longer exist, includingcdn.polyfill.io, a domain that was later compromised. Remove or vendor them before serving this app. - Debug leftovers. The login code logs its request payload, including the password, to the browser console.
- Incomplete features. Self-service password change is a stub, the message-log date filters aren't wired, list pages don't refresh after an edit, and the only unit test imports a component that doesn't exist.
- End-of-life stack. Vue 2.4, webpack 3, Bootstrap 4 beta, PhantomJS and node-sass are all long past support.