mirror of
https://github.com/fleetbase/fleetbase.git
synced 2026-01-08 07:16:49 +00:00
Compare commits
42 Commits
feature-re
...
v0.5.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc00ac3892 | ||
|
|
f18ec886a7 | ||
|
|
f039b61d79 | ||
|
|
248f70e31c | ||
|
|
6bc76a1b33 | ||
|
|
30695b3ebe | ||
|
|
7ff9c24ad5 | ||
|
|
1a9b9c06e5 | ||
|
|
5f949c3b7f | ||
|
|
0e5e4e07dd | ||
|
|
2eabfc4698 | ||
|
|
77c2c01e58 | ||
|
|
7c5b5b5858 | ||
|
|
aad072cf4c | ||
|
|
c1c6dcafd8 | ||
|
|
61992ee924 | ||
|
|
b18d6197bc | ||
|
|
04bdb52c08 | ||
|
|
8e5a45dd09 | ||
|
|
196af155ae | ||
|
|
056a717d08 | ||
|
|
fd008d7f73 | ||
|
|
451c95d0f0 | ||
|
|
b267b303cf | ||
|
|
441b4f3f0c | ||
|
|
0e4d4a7c8c | ||
|
|
24392527e0 | ||
|
|
c19d838757 | ||
|
|
3a072c1524 | ||
|
|
0c96386cf1 | ||
|
|
708babb81c | ||
|
|
8c8acf1e43 | ||
|
|
e853e2ca22 | ||
|
|
dacaff37ca | ||
|
|
b0460963e5 | ||
|
|
9ec786d892 | ||
|
|
76859aeb26 | ||
|
|
1764b804de | ||
|
|
0c33018b5b | ||
|
|
72b1b9b764 | ||
|
|
aee552f518 | ||
|
|
9967f27c83 |
18
.github/workflows/cd.yml
vendored
18
.github/workflows/cd.yml
vendored
@@ -109,13 +109,13 @@ jobs:
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
node-version: 18
|
||||
|
||||
- uses: pnpm/action-setup@v4
|
||||
name: Install pnpm
|
||||
id: pnpm-install
|
||||
with:
|
||||
version: 8
|
||||
version: 9.5.0
|
||||
run_install: false
|
||||
|
||||
- name: Get pnpm Store Directory
|
||||
@@ -139,6 +139,20 @@ jobs:
|
||||
fi
|
||||
working-directory: ./console
|
||||
|
||||
- name: Set Env Variables for QA
|
||||
if: startsWith(github.ref, 'refs/heads/deploy/qa')
|
||||
run: |
|
||||
echo "STRIPE_KEY=${{ secrets.STRIPE_TEST_KEY }}" >> ./environments/.env.production
|
||||
echo "EXTENSIONS=@fleetbase/billing-engine,@fleetbase/internals-engine" >> ./environments/.env.production
|
||||
working-directory: ./console
|
||||
|
||||
- name: Set Env Variables for Production
|
||||
if: startsWith(github.ref, 'refs/heads/deploy/production')
|
||||
run: |
|
||||
echo "STRIPE_KEY=${{ secrets.STRIPE_KEY }}" >> ./environments/.env.production
|
||||
echo "EXTENSIONS=@fleetbase/billing-engine,@fleetbase/internals-engine" >> ./environments/.env.production
|
||||
working-directory: ./console
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install
|
||||
working-directory: ./console
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@@ -36,3 +36,9 @@
|
||||
[submodule "docs"]
|
||||
path = docs
|
||||
url = git@github.com:fleetbase/docs.git
|
||||
[submodule "packages/registry-bridge"]
|
||||
path = packages/registry-bridge
|
||||
url = git@github.com:fleetbase/registry-bridge.git
|
||||
[submodule "packages/ledger"]
|
||||
path = packages/ledger
|
||||
url = git@github.com:fleetbase/ledger.git
|
||||
|
||||
15
Caddyfile
15
Caddyfile
@@ -1,19 +1,14 @@
|
||||
{
|
||||
frankenphp
|
||||
frankenphp {
|
||||
num_threads 24
|
||||
}
|
||||
order php_server before file_server
|
||||
}
|
||||
|
||||
http://:8000 {
|
||||
root * /fleetbase/api/public
|
||||
encode zstd gzip
|
||||
encode zstd br gzip
|
||||
php_server {
|
||||
resolve_root_symlink
|
||||
}
|
||||
}
|
||||
|
||||
http://:4201 {
|
||||
root * /fleetbase/console/dist
|
||||
try_files {path} /
|
||||
encode zstd gzip
|
||||
file_server
|
||||
}
|
||||
}
|
||||
19
Caddyfile.console
Normal file
19
Caddyfile.console
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
frankenphp
|
||||
order php_server before file_server
|
||||
}
|
||||
|
||||
http://:8000 {
|
||||
root * /fleetbase/api/public
|
||||
encode zstd gzip
|
||||
php_server {
|
||||
resolve_root_symlink
|
||||
}
|
||||
}
|
||||
|
||||
http://:4200 {
|
||||
root * /fleetbase/console/dist
|
||||
try_files {path} /
|
||||
encode zstd gzip
|
||||
file_server
|
||||
}
|
||||
30
README.md
30
README.md
@@ -42,6 +42,7 @@ sh deploy.sh
|
||||
|
||||
- [Features](#-features)
|
||||
- [Install](#-install)
|
||||
- [Extensions](#-extensions)
|
||||
- [Apps](#-apps)
|
||||
- [Roadmap](#-roadmap)
|
||||
- [Bugs and Feature Requests](#-bugs-and--feature-requests)
|
||||
@@ -90,7 +91,7 @@ Fleetbase API: http://localhost:8000
|
||||
|
||||
**CORS:** If you’re installing directly on a server you may need to add your IP address or domain to the `api/config/cors.php` file in the `allowed_hosts` array.
|
||||
|
||||
**Routing:** Fleetbase ships with its own OSRM server hosted at `[bundle.routing.fleetbase.io](https://bundle.routing.fleetbase.io)` but you’re able to use your own or any other OSRM compatible server. You can modify this in the `console/environments` directory by modifying the env file of the environment you’re deploying and setting the `OSRM_HOST` to the OSRM server for Fleetbase to use.
|
||||
**Routing:** Fleetbase ships with a default OSRM server hosted by `[router.project-osrm.org](https://router.project-osrm.org)` but you’re able to use your own or any other OSRM compatible server. You can modify this in the `console/environments` directory by modifying the .env file of the environment you’re deploying and setting the `OSRM_HOST` to the OSRM server for Fleetbase to use.
|
||||
|
||||
**Services:** There are a few environment variables which need to be set for Fleetbase to function with full features. If you’re deploying with docker then it’s easiest to just create a `docker-compose.override.yml` and supply the environment variables in this file.
|
||||
|
||||
@@ -100,7 +101,7 @@ services:
|
||||
application:
|
||||
environment:
|
||||
MAIL_MAILER: (ses, smtp, mailgun, postmark, sendgrid)
|
||||
OSRM_HOST: https://bundle.routing.fleetbase.io
|
||||
OSRM_HOST: https://router.project-osrm.org
|
||||
IPINFO_API_KEY:
|
||||
GOOGLE_MAPS_API_KEY:
|
||||
GOOGLE_MAPS_LOCALE: us
|
||||
@@ -112,6 +113,26 @@ services:
|
||||
|
||||
You can learn more about full installation, and configuration in the [official documentation](https://docs.fleetbase.io/getting-started/install).
|
||||
|
||||
# 🧩 Extensions
|
||||
|
||||
Extensions are modular components that enhance the functionality of your Fleetbase instance. They allow you to add new features, customize existing behavior, or integrate with external systems.
|
||||
|
||||
You can find extensions available from the official [Fleetbase Console](https://console.fleetbase.io), here you will also be able get your registry token to install extensions to a self-hosted Fleetbase instance.
|
||||
|
||||
Additionally you're able to develop and publish your own extensions as well which you can read more about developing extensions via the [extension building guide](https://docs.fleetbase.io/developers/building-an-extension).
|
||||
|
||||
## ⌨️ Fleetbase CLI
|
||||
|
||||
The Fleetbase CLI is a powerful tool designed to simplify the management of extensions for your Fleetbase instance. With the CLI, you can effortlessly handle authentication, install and uninstall extensions, and scaffold new extensions if you are developing your own.
|
||||
|
||||
Get started with the CLI with npm:
|
||||
|
||||
```bash
|
||||
npm i -g @fleetbase/cli
|
||||
```
|
||||
|
||||
Once installed, you can access a variety of commands to manage your Fleetbase extensions.
|
||||
|
||||
# 📱 Apps
|
||||
|
||||
Fleetbase offers a few open sourced apps which are built on Fleetbase which can be cloned and customized. Every app is built so that the Fleetbase instance can be switched out whether on-premise install or cloud hosted.
|
||||
@@ -122,11 +143,10 @@ Fleetbase offers a few open sourced apps which are built on Fleetbase which can
|
||||
</ul>
|
||||
|
||||
## 🛣️ Roadmap
|
||||
1. **Extensions Registry and Marketplace** ~ Allows users to publish and sell installable extensions on Fleetbase instances.
|
||||
1. **Customer Facing Views** ~ Extensions will be able to create public/customer facing views tracking and management from outside of the console UI.
|
||||
2. **Inventory and Warehouse Management** ~ Pallet will be Fleetbase’s first official extension for WMS & Inventory.
|
||||
3. **Customer Facing Views** ~ Extensions will be able to create public/customer facing views tracking and management from outside of the console UI.
|
||||
3. **Accounting and Invoicing** ~ Pallet will be Fleetbase’s first official extension for WMS & Inventory.
|
||||
4. **Binary Builds** ~ Run Fleetbase from a single binary.
|
||||
5. **Fleetbase CLI** ~ Official CLI for publishing and managing extensions, as well as scaffolding extensions.
|
||||
6. **Fleetbase for Desktop** ~ Desktop builds for OSX and Windows.
|
||||
7. **Custom Maps and Routing Engines** ~ Feature to enable easy integrations with custom maps and routing engines like Google Maps or Mapbox etc…
|
||||
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
"license": "AGPL-3.0-or-later",
|
||||
"require": {
|
||||
"php": "^8.0",
|
||||
"fleetbase/core-api": "^1.4.27",
|
||||
"fleetbase/fleetops-api": "^0.5.3",
|
||||
"fleetbase/storefront-api": "^0.3.12",
|
||||
"fleetbase/core-api": "^1.5.5",
|
||||
"fleetbase/fleetops-api": "^0.5.7",
|
||||
"fleetbase/registry-bridge": "^0.0.13",
|
||||
"fleetbase/storefront-api": "^0.3.14",
|
||||
"guzzlehttp/guzzle": "^7.0.1",
|
||||
"laravel/framework": "^10.0",
|
||||
"laravel/octane": "^2.3",
|
||||
@@ -36,6 +37,10 @@
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/fleetbase/laravel-model-caching"
|
||||
},
|
||||
{
|
||||
"type": "composer",
|
||||
"url": "https://registry.fleetbase.io"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
|
||||
1356
api/composer.lock
generated
1356
api/composer.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -15,6 +15,9 @@ php artisan sandbox:migrate --force
|
||||
# Seed database
|
||||
php artisan fleetbase:seed
|
||||
|
||||
# Create permissions, policies, and roles
|
||||
php artisan fleetbase:create-permissions
|
||||
|
||||
# Restart queue
|
||||
php artisan queue:restart
|
||||
|
||||
@@ -23,3 +26,6 @@ php artisan schedule-monitor:sync
|
||||
|
||||
# Clear cache
|
||||
php artisan cache:clear
|
||||
|
||||
# Initialize registry
|
||||
php artisan registry:init
|
||||
|
||||
54
console/Dockerfile.server-build
Normal file
54
console/Dockerfile.server-build
Normal file
@@ -0,0 +1,54 @@
|
||||
# ---- Build Stage ----
|
||||
FROM node:18.15.0-alpine as builder
|
||||
|
||||
# Set the working directory in the container to /console
|
||||
WORKDIR /console
|
||||
|
||||
# Create the pnpm directory and set the PNPM_HOME environment variable
|
||||
RUN mkdir -p ~/.pnpm
|
||||
ENV PNPM_HOME /root/.pnpm
|
||||
|
||||
# Set environment
|
||||
ARG ENVIRONMENT=production
|
||||
|
||||
# Add the pnpm global bin to the PATH
|
||||
ENV PATH /root/.pnpm/bin:$PATH
|
||||
|
||||
# Copy pnpm-lock.yaml (or package.json) into the directory /console in the container
|
||||
COPY console/package.json console/pnpm-lock.yaml ./
|
||||
|
||||
# Copy over .npmrc if applicable
|
||||
COPY console/.npmr[c] ./
|
||||
|
||||
# Install global dependencies
|
||||
RUN npm install -g ember-cli pnpm
|
||||
|
||||
# Install git
|
||||
RUN apk update && apk add git openssh-client
|
||||
|
||||
# Trust GitHub's RSA host key
|
||||
RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
|
||||
|
||||
# Install app dependencies
|
||||
RUN pnpm install
|
||||
|
||||
# Copy the console directory contents into the container at /console
|
||||
COPY console .
|
||||
|
||||
# Build the application
|
||||
RUN pnpm build --environment $ENVIRONMENT
|
||||
|
||||
# ---- Serve Stage ----
|
||||
FROM nginx:alpine
|
||||
|
||||
# Copy the built app to our served directory
|
||||
COPY --from=builder /console/dist /usr/share/nginx/html
|
||||
|
||||
# Expose the port nginx is bound to
|
||||
EXPOSE 4200
|
||||
|
||||
# Use custom nginx.conf
|
||||
COPY console/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Start Nginx server
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -1,8 +1,8 @@
|
||||
# ---- Build Stage ----
|
||||
FROM node:18.15.0-alpine AS builder
|
||||
FROM node:18.15.0-alpine
|
||||
|
||||
# Set the working directory in the container to /app
|
||||
WORKDIR /app
|
||||
# Set the working directory in the container to /console
|
||||
WORKDIR /console
|
||||
|
||||
# Create the pnpm directory and set the PNPM_HOME environment variable
|
||||
RUN mkdir -p ~/.pnpm
|
||||
@@ -14,7 +14,7 @@ ARG ENVIRONMENT=production
|
||||
# Add the pnpm global bin to the PATH
|
||||
ENV PATH /root/.pnpm/bin:$PATH
|
||||
|
||||
# Copy pnpm-lock.yaml (or package.json) into the directory /app in the container
|
||||
# Copy pnpm-lock.yaml (or package.json) into the directory /console in the container
|
||||
COPY console/package.json console/pnpm-lock.yaml ./
|
||||
|
||||
# Copy over .npmrc if applicable
|
||||
@@ -32,23 +32,26 @@ RUN mkdir -p -m 0600 ~/.ssh && ssh-keyscan github.com >> ~/.ssh/known_hosts
|
||||
# Install app dependencies
|
||||
RUN pnpm install
|
||||
|
||||
# Copy the console directory contents into the container at /app
|
||||
# Copy the console directory contents into the container at /console
|
||||
COPY console .
|
||||
|
||||
# Build the application
|
||||
RUN pnpm build --environment $ENVIRONMENT
|
||||
|
||||
# ---- Serve Stage ----
|
||||
FROM nginx:alpine
|
||||
# # Make sure the build output is available in /console/dist
|
||||
# RUN ls -la /console/dist
|
||||
|
||||
# Copy the built app to our served directory
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
# # ---- Serve Stage ----
|
||||
# FROM nginx:alpine
|
||||
|
||||
# Expose the port nginx is bound to
|
||||
EXPOSE 4200
|
||||
# # Copy the built app to our served directory
|
||||
# COPY --from=builder /console/dist /usr/share/nginx/html
|
||||
|
||||
# Use custom nginx.conf
|
||||
COPY console/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
# # Expose the port nginx is bound to
|
||||
# EXPOSE 4201
|
||||
|
||||
# Start Nginx server
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
# # Use custom nginx.conf
|
||||
# COPY console/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# # Start Nginx server
|
||||
# CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -106,10 +106,8 @@ export default class ConfigureFilesystemComponent extends Component {
|
||||
type: 'gcs_credentials',
|
||||
},
|
||||
(uploadedFile) => {
|
||||
console.log('uploadedFile', uploadedFile);
|
||||
this.gcsCredentialsFileId = uploadedFile.id;
|
||||
this.gcsCredentialsFile = uploadedFile;
|
||||
console.log('this.gcsCredentialsFile', this.gcsCredentialsFile);
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
{{/if}}
|
||||
<div class="flex flex-row items-center mt-3">
|
||||
<Input @value={{this.twilioTestPhone}} @type="tel" placeholder="Send Test SMS Here" class="form-input form-input-sm" />
|
||||
<Button @wrapperClass="ml-2" @icon="plug" @text="Test Twilio Config" @onClick={{this.testTwilio}} @isLoading={{this.isLoading}} @disabled={{not this.twilioTestPhone}} />
|
||||
<Button @wrapperClass="ml-2" @icon="plug" @text="Test Twilio Config" @onClick={{perform this.testTwilio}} @isLoading={{this.testTwilio.isRunning}} @disabled={{not this.twilioTestPhone}} />
|
||||
</div>
|
||||
</ContentPanel>
|
||||
|
||||
@@ -33,13 +33,15 @@
|
||||
<span class="text-xs">{{this.this.sentryTestResponse.message}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
<Button @wrapperClass="mt-3" @icon="plug" @text="Test Sentry Config" @onClick={{this.testSentry}} @isLoading={{this.isLoading}} />
|
||||
<Button @wrapperClass="mt-3" @icon="plug" @text="Test Sentry Config" @onClick={{perform this.testSentry}} @isLoading={{this.testSentry.isRunning}} @disabled={{not this.sentryDsn}} />
|
||||
</ContentPanel>
|
||||
|
||||
<ContentPanel @title="IP Info" @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<InputGroup @name="IP Info API Key" @value={{this.ipinfoApiKey}} disabled={{this.isLoading}} />
|
||||
</ContentPanel>
|
||||
|
||||
<Spacer @height="200px" />
|
||||
|
||||
<EmberWormhole @to="next-view-section-subheader-actions">
|
||||
<Button @type="primary" @size="sm" @icon="save" @text="Save Changes" @onClick={{this.save}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
|
||||
<Button @type="primary" @size="sm" @icon="save" @text="Save Changes" @onClick={{perform this.save}} @disabled={{or this.save.isRunning this.loadConfigValues.isRunning}} @isLoading={{or this.save.isRunning this.loadConfigValues.isRunning}} />
|
||||
</EmberWormhole>
|
||||
@@ -2,6 +2,7 @@ import Component from '@glimmer/component';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { task } from 'ember-concurrency';
|
||||
|
||||
export default class ConfigureServicesComponent extends Component {
|
||||
@service fetch;
|
||||
@@ -37,7 +38,7 @@ export default class ConfigureServicesComponent extends Component {
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.loadConfigValues();
|
||||
this.loadConfigValues.perform();
|
||||
}
|
||||
|
||||
@action setConfigValues(config) {
|
||||
@@ -48,24 +49,19 @@ export default class ConfigureServicesComponent extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
@action loadConfigValues() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.get('settings/services-config')
|
||||
.then((response) => {
|
||||
this.setConfigValues(response);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
@task *loadConfigValues() {
|
||||
try {
|
||||
const config = yield this.fetch.get('settings/services-config');
|
||||
this.setConfigValues(config);
|
||||
return config;
|
||||
} catch (error) {
|
||||
this.notifications.serverError(error);
|
||||
}
|
||||
}
|
||||
|
||||
@action save() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/services-config', {
|
||||
@task *save() {
|
||||
try {
|
||||
yield this.fetch.post('settings/services-config', {
|
||||
aws: {
|
||||
key: this.awsKey,
|
||||
secret: this.awsSecret,
|
||||
@@ -86,45 +82,36 @@ export default class ConfigureServicesComponent extends Component {
|
||||
sentry: {
|
||||
dsn: this.sentryDsn,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
this.notifications.success('Services configuration saved.');
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
} catch (error) {
|
||||
this.notifications.serverError(error);
|
||||
}
|
||||
}
|
||||
|
||||
@action testTwilio() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/test-twilio-config', {
|
||||
@task *testTwilio() {
|
||||
try {
|
||||
const twilioTestResponse = yield this.fetch.post('settings/test-twilio-config', {
|
||||
sid: this.twilioSid,
|
||||
token: this.twilioToken,
|
||||
from: this.twilioFrom,
|
||||
phone: this.twilioTestPhone,
|
||||
})
|
||||
.then((response) => {
|
||||
this.twilioTestResponse = response;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
this.twilioTestResponse = twilioTestResponse;
|
||||
return twilioTestResponse;
|
||||
} catch (error) {
|
||||
this.notifications.serverError(error);
|
||||
}
|
||||
}
|
||||
|
||||
@action testSentry() {
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('settings/test-sentry-config', {
|
||||
@task *testSentry() {
|
||||
try {
|
||||
const sentryTestResponse = yield this.fetch.post('settings/test-sentry-config', {
|
||||
dsn: this.sentryDsn,
|
||||
})
|
||||
.then((response) => {
|
||||
this.sentryTestResponse = response;
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
this.sentryTestResponse = sentryTestResponse;
|
||||
return sentryTestResponse;
|
||||
} catch (error) {
|
||||
this.notifications.serverError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,10 @@ export default class DashboardCountComponent extends Component {
|
||||
* @param {Object} { options }
|
||||
* @memberof WidgetKeyMetricsCountComponent
|
||||
*/
|
||||
constructor(owner, { options, title }) {
|
||||
constructor(owner, { options, title, value = null }) {
|
||||
super(...arguments);
|
||||
this.title = title;
|
||||
this.value = value;
|
||||
this.createRenderValueFromOptions(options);
|
||||
}
|
||||
|
||||
@@ -40,6 +41,10 @@ export default class DashboardCountComponent extends Component {
|
||||
* @memberof WidgetKeyMetricsCountComponent
|
||||
*/
|
||||
createRenderValueFromOptions(options = {}) {
|
||||
if (value !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let { format, currency, dateFormat, value } = options;
|
||||
|
||||
switch (format) {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
<div class="fleetbase-dashboard-grid" ...attributes>
|
||||
<GridStack @options={{this.gridOptions}} @onChange={{this.onChangeGrid}}>
|
||||
{{#each @dashboard.widgets as |widget|}}
|
||||
<GridStackItem id={{widget.id}} @options={{spread-widget-options (hash id=widget.id options=widget.grid_options)}} class="relative">
|
||||
{{component widget.component options=widget.options}}
|
||||
{{#if @isEdit}}
|
||||
<div class="absolute top-2 right-2">
|
||||
<Button @type="default" @icon="trash" @helpText={{"Remove widget from the dashboard"}} @onClick={{fn this.removeWidget widget}} />
|
||||
</div>
|
||||
{{/if}}
|
||||
</GridStackItem>
|
||||
{{#if (component-resolvable widget.component)}}
|
||||
<GridStackItem id={{widget.id}} @options={{spread-widget-options (hash id=widget.id options=widget.grid_options)}} class="relative">
|
||||
{{component widget.component options=widget.options}}
|
||||
{{#if @isEdit}}
|
||||
<div class="absolute top-2 right-2">
|
||||
<Button @type="default" @icon="trash" @helpText={{"Remove widget from the dashboard"}} @onClick={{fn this.removeWidget widget}} />
|
||||
</div>
|
||||
{{/if}}
|
||||
</GridStackItem>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
</GridStack>
|
||||
</div>
|
||||
@@ -1 +0,0 @@
|
||||
{{yield}}
|
||||
@@ -1,191 +0,0 @@
|
||||
import Component from '@glimmer/component';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { isArray } from '@ember/array';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { later } from '@ember/runloop';
|
||||
import { task, timeout } from 'ember-concurrency';
|
||||
import { injectAsset } from '../utils/asset-injector';
|
||||
import getMountedEngineRoutePrefix from '@fleetbase/ember-core/utils/get-mounted-engine-route-prefix';
|
||||
|
||||
function removeTrailingDot (str) {
|
||||
if (str.endsWith('.')) {
|
||||
return str.slice(0, -1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
window.exports = window.exports ?? {};
|
||||
export default class ExtensionInjectorComponent extends Component {
|
||||
@service fetch;
|
||||
@service notifications;
|
||||
@service universe;
|
||||
@tracked engines = [];
|
||||
@tracked packages = [];
|
||||
|
||||
constructor () {
|
||||
super(...arguments);
|
||||
this.loadInstalledEngines.perform();
|
||||
}
|
||||
|
||||
@task *loadInstalledEngines () {
|
||||
yield timeout(300);
|
||||
|
||||
try {
|
||||
const engines = yield this.fetch.get('load-installed-engines', {}, { namespace: '~registry/v1' });
|
||||
for (const id in engines) {
|
||||
yield this.loadAndMountEngine.perform(id, engines[id]);
|
||||
}
|
||||
} catch (error) {
|
||||
this.notifications.serverError(error);
|
||||
}
|
||||
}
|
||||
|
||||
@task *loadAndMountEngine (id, enginePackage) {
|
||||
const engineName = enginePackage.name;
|
||||
const assets = yield this.fetch.get(`load-engine-manifest/${id}`, {}, { namespace: '~registry/v1' });
|
||||
|
||||
if (isArray(assets)) {
|
||||
for (const i in assets) {
|
||||
injectAsset(assets[i]);
|
||||
}
|
||||
}
|
||||
|
||||
yield timeout(300);
|
||||
this.registerEngine(enginePackage);
|
||||
}
|
||||
|
||||
registerEngine (enginePackage) {
|
||||
const engineName = enginePackage.name;
|
||||
const owner = getOwner(this);
|
||||
const router = getOwner(this).lookup('router:main');
|
||||
|
||||
if (this.hasAssetManifest(engineName)) {
|
||||
return this.universe.loadEngine(engineName).then(engineInstance => {
|
||||
if (engineInstance.base && engineInstance.base.setupExtension) {
|
||||
engineInstance.base.setupExtension(owner, engineInstance, this.universe);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
if (router._engineIsLoaded(engineName)) {
|
||||
router._registerEngine(engineName);
|
||||
|
||||
const instanceId = Object.values(router._engineInstances).length;
|
||||
const mountPoint = removeTrailingDot(getMountedEngineRoutePrefix(engineName.replace('@fleetbase/', ''), enginePackage.fleetbase));
|
||||
this.universe.constructEngineInstance(engineName, instanceId, mountPoint).then(engineInstance => {
|
||||
if (engineInstance) {
|
||||
this.setupEngine(owner, engineInstance, enginePackage);
|
||||
this.setEnginePromise(engineName, engineInstance);
|
||||
this.addEngineRoutesToRouter(engineName, engineInstance, instanceId, mountPoint, router);
|
||||
this.addEngineRoutesToRecognizer(engineName, router);
|
||||
this.resolveEngineRegistrations(engineInstance);
|
||||
|
||||
console.log(engineInstance, router, owner);
|
||||
if (engineInstance.base && engineInstance.base.setupExtension) {
|
||||
engineInstance.base.setupExtension(owner, engineInstance, this.universe);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.trace(error);
|
||||
}
|
||||
}
|
||||
|
||||
setupEngine (appInstance, engineInstance, enginePackage) {
|
||||
const engineName = enginePackage.name;
|
||||
appInstance.application.engines[engineName] = engineInstance.dependencies ?? { externalRoutes: {}, services: {} };
|
||||
appInstance._dependenciesForChildEngines[engineName] = engineInstance.dependencies ?? { externalRoutes: {}, services: {} };
|
||||
if (isArray(appInstance.application.extensions)) {
|
||||
appInstance.application.extensions.push(enginePackage);
|
||||
}
|
||||
}
|
||||
|
||||
addEngineRoutesToRecognizer (engineName, router) {
|
||||
const recognizer = router._routerMicrolib.recognizer || router._router._routerMicrolib.recognizer;
|
||||
if (recognizer) {
|
||||
let routeName = `${engineName}.application`;
|
||||
|
||||
recognizer.add(
|
||||
[
|
||||
{
|
||||
path: 'console.fleet-ops',
|
||||
handler: 'console.fleet-ops',
|
||||
},
|
||||
],
|
||||
{ as: 'console.fleet-ops' }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
addEngineRoutesToRouter (engineName, engineInstance, instanceId, mountPoint, router) {
|
||||
const getRouteInfo = routeName => {
|
||||
const applicationRoute = routeName.replace(mountPoint, '') === '';
|
||||
return {
|
||||
fullName: mountPoint,
|
||||
instanceId,
|
||||
localFullName: applicationRoute ? 'application' : routeName.replace(`${mountPoint}.`, ''),
|
||||
mountPoint,
|
||||
name: engineName,
|
||||
};
|
||||
};
|
||||
|
||||
const routes = ['console.fleet-ops', 'console.fleet-ops.home'];
|
||||
for (let i = 0; i < routes.length; i++) {
|
||||
const routeName = routes[i];
|
||||
router._engineInfoByRoute[routeName] = getRouteInfo(routeName);
|
||||
}
|
||||
|
||||
// Reinitialize or refresh the router
|
||||
router.setupRouter();
|
||||
}
|
||||
|
||||
setEnginePromise (engineName, engineInstance) {
|
||||
const router = getOwner(this).lookup('router:main');
|
||||
if (router) {
|
||||
router._enginePromises[engineName] = { manual: engineInstance._bootPromise };
|
||||
}
|
||||
}
|
||||
|
||||
resolveEngineRegistrations (engineInstance) {
|
||||
const owner = getOwner(this);
|
||||
const registry = engineInstance.__registry__;
|
||||
const registrations = registry.registrations;
|
||||
const getOwnerSymbol = obj => {
|
||||
const symbols = Object.getOwnPropertySymbols(obj);
|
||||
const ownerSymbol = symbols.find(symbol => symbol.toString() === 'Symbol(OWNER)');
|
||||
return ownerSymbol;
|
||||
};
|
||||
for (let registrationKey in registrations) {
|
||||
const registrationInstance = registrations[registrationKey];
|
||||
if (typeof registrationInstance === 'string') {
|
||||
// Try to resolve from owner
|
||||
let resolvedRegistrationInstance = owner.lookup(registrationKey);
|
||||
// Hack for host-router
|
||||
if (registrationKey === 'service:host-router') {
|
||||
resolvedRegistrationInstance = owner.lookup('service:router');
|
||||
}
|
||||
if (resolvedRegistrationInstance) {
|
||||
// Correct the owner
|
||||
resolvedRegistrationInstance[getOwnerSymbol(resolvedRegistrationInstance)] = engineInstance;
|
||||
// Resolve
|
||||
registrations[registrationKey] = resolvedRegistrationInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hasAssetManifest (engineName) {
|
||||
const router = getOwner(this).lookup('router:main');
|
||||
if (router._assetLoader) {
|
||||
const manifest = router._assetLoader.getManifest();
|
||||
if (manifest && manifest.bundles) {
|
||||
return manifest.bundles[engineName] !== undefined;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="fleetbase-github-card relative flex-1 w-full" ...attributes>
|
||||
<div class="border dark:border-gray-700 border-gray-200 dark:bg-gray-800 bg-gray-50 rounded-lg shadow-sm flex flex-col">
|
||||
{{#if this.isLoading}}
|
||||
{{#if this.getRepositoryData.isRunning}}
|
||||
<div class="p-4">
|
||||
<Spinner />
|
||||
</div>
|
||||
|
||||
@@ -10,9 +10,12 @@ import fetch from 'fetch';
|
||||
|
||||
export default class GithubCardComponent extends Component {
|
||||
@storageFor('local-cache') localCache;
|
||||
@tracked data;
|
||||
@tracked tags;
|
||||
@tracked isLoading = false;
|
||||
@tracked data = {
|
||||
owner: {
|
||||
avatar_url: 'https://avatars.githubusercontent.com/u/38091894?v=4',
|
||||
},
|
||||
};
|
||||
@tracked tags = [];
|
||||
|
||||
@computed('tags.length') get latestRelease() {
|
||||
if (isArray(this.tags) && this.tags.length) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { action } from '@ember/object';
|
||||
import { task } from 'ember-concurrency';
|
||||
|
||||
export default class AuthForgotPasswordController extends Controller {
|
||||
/**
|
||||
@@ -32,13 +32,6 @@ export default class AuthForgotPasswordController extends Controller {
|
||||
*/
|
||||
@tracked email;
|
||||
|
||||
/**
|
||||
* The loading state
|
||||
*
|
||||
* @memberof AuthForgotPasswordController
|
||||
*/
|
||||
@tracked isLoading;
|
||||
|
||||
/**
|
||||
* Indicator if request has been sent.
|
||||
*
|
||||
@@ -46,30 +39,27 @@ export default class AuthForgotPasswordController extends Controller {
|
||||
*/
|
||||
@tracked isSent = false;
|
||||
|
||||
/**
|
||||
* Query parameters.
|
||||
*
|
||||
* @memberof AuthForgotPasswordController
|
||||
*/
|
||||
queryParams = ['email'];
|
||||
|
||||
/**
|
||||
* Sends a secure magic reset link to the user provided email.
|
||||
*
|
||||
* @memberof AuthForgotPasswordController
|
||||
*/
|
||||
@action sendSecureLink(event) {
|
||||
// firefox patch
|
||||
@task *sendSecureLink(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const { email } = this;
|
||||
|
||||
this.isLoading = true;
|
||||
|
||||
this.fetch
|
||||
.post('auth/get-magic-reset-link', { email })
|
||||
.then(() => {
|
||||
this.notifications.success(this.intl.t('auth.forgot-password.success-message'));
|
||||
this.isSent = true;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
})
|
||||
.finally(() => {
|
||||
this.isLoading = false;
|
||||
});
|
||||
try {
|
||||
yield this.fetch.post('auth/get-magic-reset-link', { email: this.email });
|
||||
this.notifications.success(this.intl.t('auth.forgot-password.success-message'));
|
||||
this.isSent = true;
|
||||
} catch (error) {
|
||||
this.notifications.serverError(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,6 +166,11 @@ export default class AuthLoginController extends Controller {
|
||||
return this.sendUserForEmailVerification(identity);
|
||||
}
|
||||
|
||||
// Handle password reset required
|
||||
if (error.toString().includes('reset required')) {
|
||||
return this.sendUserForPasswordReset(identity);
|
||||
}
|
||||
|
||||
return this.failure(error);
|
||||
}
|
||||
|
||||
@@ -210,6 +215,20 @@ export default class AuthLoginController extends Controller {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends user to forgot password flow.
|
||||
*
|
||||
* @param {String} email
|
||||
* @return {Promise<Transition>}
|
||||
* @memberof AuthLoginController
|
||||
*/
|
||||
@action sendUserForPasswordReset(email) {
|
||||
this.notifications.warning(this.intl.t('auth.login.password-reset-required'));
|
||||
return this.router.transitionTo('auth.forgot-password', { queryParams: { email } }).then(() => {
|
||||
this.reset('error');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets correct route to send user to after login.
|
||||
*
|
||||
|
||||
@@ -53,6 +53,13 @@ export default class AuthResetPasswordController extends Controller {
|
||||
*/
|
||||
@tracked password_confirmation;
|
||||
|
||||
/**
|
||||
* Query parameters.
|
||||
*
|
||||
* @memberof AuthResetPasswordController
|
||||
*/
|
||||
queryParams = ['code'];
|
||||
|
||||
/**
|
||||
* The reset password task.
|
||||
*
|
||||
|
||||
@@ -1,69 +1,21 @@
|
||||
import Controller from '@ember/controller';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { later } from '@ember/runloop';
|
||||
import { action, computed } from '@ember/object';
|
||||
import { alias } from '@ember/object/computed';
|
||||
import { action } from '@ember/object';
|
||||
import { isArray } from '@ember/array';
|
||||
import first from '@fleetbase/ember-core/utils/first';
|
||||
|
||||
export default class ConsoleController extends Controller {
|
||||
/**
|
||||
* Inject the `currentUser` service.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service currentUser;
|
||||
|
||||
/**
|
||||
* Inject the `modalsManager` service.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service modalsManager;
|
||||
|
||||
/**
|
||||
* Inject the `session` service.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service session;
|
||||
|
||||
/**
|
||||
* Inject the `fetch` service.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Inject the `notifications` service.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service notifications;
|
||||
|
||||
/**
|
||||
* Inject the `router` service.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service router;
|
||||
|
||||
/**
|
||||
* Inject the `intl` service.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service intl;
|
||||
|
||||
/**
|
||||
* Inject the `universe` service.
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service universe;
|
||||
@service abilities;
|
||||
|
||||
/**
|
||||
* Authenticated user organizations.
|
||||
@@ -98,23 +50,28 @@ export default class ConsoleController extends Controller {
|
||||
*
|
||||
* @var {Array}
|
||||
*/
|
||||
@tracked hiddenSidebarRoutes = ['console.home', 'console.extensions', 'console.notifications'];
|
||||
@tracked hiddenSidebarRoutes = ['console.home', 'console.notifications'];
|
||||
|
||||
/**
|
||||
* Installed extensions.
|
||||
* Menu items to be added to the main header navigation bar.
|
||||
*
|
||||
* @var {Array}
|
||||
* @memberof ConsoleController
|
||||
*/
|
||||
@computed() get extensions() {
|
||||
return getOwner(this).application.extensions;
|
||||
}
|
||||
@tracked menuItems = [];
|
||||
|
||||
/**
|
||||
* Get the currently authenticated user
|
||||
* Menu items to be added to the user dropdown menu located in the header.
|
||||
*
|
||||
* @var {Model}
|
||||
* @memberof ConsoleController
|
||||
*/
|
||||
@alias('currentUser.user') user;
|
||||
@tracked userMenuItems = [];
|
||||
|
||||
/**
|
||||
* Menu items to be added to the organization dropdown menu located in the header.
|
||||
*
|
||||
* @memberof ConsoleController
|
||||
*/
|
||||
@tracked organizationMenuItems = [];
|
||||
|
||||
/**
|
||||
* Creates an instance of ConsoleController.
|
||||
@@ -228,53 +185,51 @@ export default class ConsoleController extends Controller {
|
||||
changeAction: (action) => {
|
||||
this.modalsManager.setOption('action', action);
|
||||
},
|
||||
confirm: (modal) => {
|
||||
confirm: async (modal) => {
|
||||
modal.startLoading();
|
||||
|
||||
const { action, next, name, description, phone, currency, country, timezone } = modal.getOptions();
|
||||
|
||||
if (action === 'join') {
|
||||
return this.fetch
|
||||
.post('auth/join-organization', { next })
|
||||
.then(() => {
|
||||
this.fetch.flushRequestCache('auth/organizations');
|
||||
this.notifications.success(this.intl.t('console.create-or-join-organization.join-success-notification'));
|
||||
later(
|
||||
this,
|
||||
() => {
|
||||
window.location.reload();
|
||||
},
|
||||
900
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
});
|
||||
}
|
||||
|
||||
return this.fetch
|
||||
.post('auth/create-organization', {
|
||||
name,
|
||||
description,
|
||||
phone,
|
||||
currency,
|
||||
country,
|
||||
timezone,
|
||||
})
|
||||
.then(() => {
|
||||
try {
|
||||
await this.fetch.post('auth/join-organization', { next });
|
||||
this.fetch.flushRequestCache('auth/organizations');
|
||||
this.notifications.success(this.intl.t('console.create-or-join-organization.create-success-notification'));
|
||||
later(
|
||||
this.notifications.success(this.intl.t('console.create-or-join-organization.join-success-notification'));
|
||||
return later(
|
||||
this,
|
||||
() => {
|
||||
window.location.reload();
|
||||
},
|
||||
900
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
} catch (error) {
|
||||
modal.stopLoading();
|
||||
return this.notifications.serverError(error);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.fetch.post('auth/create-organization', {
|
||||
name,
|
||||
description,
|
||||
phone,
|
||||
currency,
|
||||
country,
|
||||
timezone,
|
||||
});
|
||||
this.fetch.flushRequestCache('auth/organizations');
|
||||
this.notifications.success(this.intl.t('console.create-or-join-organization.create-success-notification'));
|
||||
return later(
|
||||
this,
|
||||
() => {
|
||||
window.location.reload();
|
||||
},
|
||||
900
|
||||
);
|
||||
} catch (error) {
|
||||
modal.stopLoading();
|
||||
return this.notifications.serverError(error);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -294,25 +249,24 @@ export default class ConsoleController extends Controller {
|
||||
body: this.intl.t('console.switch-organization.modal-body'),
|
||||
acceptButtonText: this.intl.t('console.switch-organization.modal-accept-button-text'),
|
||||
acceptButtonScheme: 'primary',
|
||||
confirm: (modal) => {
|
||||
confirm: async (modal) => {
|
||||
modal.startLoading();
|
||||
|
||||
return this.fetch
|
||||
.post('auth/switch-organization', { next: organization.uuid })
|
||||
.then(() => {
|
||||
this.fetch.flushRequestCache('auth/organizations');
|
||||
this.notifications.success(this.intl.t('console.switch-organization.success-notification'));
|
||||
later(
|
||||
this,
|
||||
() => {
|
||||
window.location.reload();
|
||||
},
|
||||
900
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
this.notifications.serverError(error);
|
||||
});
|
||||
try {
|
||||
await this.fetch.post('auth/switch-organization', { next: organization.uuid });
|
||||
this.fetch.flushRequestCache('auth/organizations');
|
||||
this.notifications.success(this.intl.t('console.switch-organization.success-notification'));
|
||||
return later(
|
||||
this,
|
||||
() => {
|
||||
window.location.reload();
|
||||
},
|
||||
900
|
||||
);
|
||||
} catch (error) {
|
||||
modal.stopLoading();
|
||||
return this.notifications.serverError(error);
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export function initialize (owner) {
|
||||
export function initialize(owner) {
|
||||
const universe = owner.lookup('service:universe');
|
||||
if (universe) {
|
||||
universe.createRegistry('@fleetbase/console');
|
||||
universe.bootEngines(owner);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import config from 'ember-get-config';
|
||||
|
||||
export function initialize(owner) {
|
||||
const universe = owner.lookup('service:universe');
|
||||
|
||||
if (universe) {
|
||||
universe.registerOrganizationMenuItem(`v${config.version}`, {
|
||||
index: 4,
|
||||
route: null,
|
||||
icon: 'code-branch',
|
||||
iconSize: 'xs',
|
||||
iconClass: 'mr-1.5',
|
||||
wrapperClass: 'app-version-in-nav',
|
||||
overwriteWrapperClass: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default {
|
||||
initialize,
|
||||
};
|
||||
@@ -1,5 +1,6 @@
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
import { computed } from '@ember/object';
|
||||
import { capitalize } from '@ember/string';
|
||||
import { pluralize } from 'ember-inflector';
|
||||
import { format, formatDistanceToNow } from 'date-fns';
|
||||
import humanize from '@fleetbase/ember-core/utils/humanize';
|
||||
@@ -26,14 +27,27 @@ export const getPermissionResource = function (permissionName) {
|
||||
return parserPermissionName(permissionName, 2);
|
||||
};
|
||||
|
||||
const lowercase = function (string) {
|
||||
let words = string.split(' ');
|
||||
words[0] = words[0].toLowerCase();
|
||||
return words.join(' ');
|
||||
const titleize = function (string = '') {
|
||||
if (typeof string !== 'string') {
|
||||
return '';
|
||||
}
|
||||
return humanize(string)
|
||||
.split(' ')
|
||||
.map((w) => capitalize(w))
|
||||
.join(' ');
|
||||
};
|
||||
|
||||
const titleize = function (string) {
|
||||
return lowercase(humanize(string));
|
||||
const smartTitleize = function (string = '') {
|
||||
if (typeof string !== 'string') {
|
||||
return '';
|
||||
}
|
||||
|
||||
let titleized = titleize(string);
|
||||
if (titleized === 'Iam') {
|
||||
titleized = titleized.toUpperCase();
|
||||
}
|
||||
|
||||
return titleized;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -49,6 +63,7 @@ export default class PermissionModel extends Model {
|
||||
/** @attributes */
|
||||
@attr('string') name;
|
||||
@attr('string') guard_name;
|
||||
@attr('string') service;
|
||||
|
||||
/** @dates */
|
||||
@attr('date') created_at;
|
||||
@@ -59,6 +74,7 @@ export default class PermissionModel extends Model {
|
||||
return {
|
||||
name: this.name,
|
||||
guard_name: this.guard_name,
|
||||
service: this.service,
|
||||
created_at: this.created_at,
|
||||
updated_at: this.updated_at,
|
||||
};
|
||||
@@ -80,6 +96,10 @@ export default class PermissionModel extends Model {
|
||||
return 'do anything';
|
||||
}
|
||||
|
||||
if (action === 'see') {
|
||||
return 'Visibly See';
|
||||
}
|
||||
|
||||
return titleize(action);
|
||||
}
|
||||
|
||||
@@ -90,9 +110,9 @@ export default class PermissionModel extends Model {
|
||||
@computed('actionName', 'name', 'resourceName', 'extensionName') get description() {
|
||||
let actionName = this.actionName;
|
||||
let actionPreposition = 'to';
|
||||
let resourceName = pluralize(humanize(this.resourceName));
|
||||
let resourceName = pluralize(smartTitleize(this.resourceName));
|
||||
let resourcePreposition = getPermissionAction(this.name) === '*' && resourceName ? 'with' : '';
|
||||
let extensionName = humanize(this.extensionName);
|
||||
let extensionName = smartTitleize(this.extensionName);
|
||||
let extensionPreposition = 'on';
|
||||
let descriptionParts = ['Permission', actionPreposition, actionName, resourcePreposition, resourceName, extensionPreposition, extensionName];
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ export default class PolicyModel extends Model {
|
||||
/** @attributes */
|
||||
@attr('string') name;
|
||||
@attr('string') type;
|
||||
@attr('string') service;
|
||||
@attr('string') guard_name;
|
||||
@attr('string') description;
|
||||
@attr('boolean') is_mutable;
|
||||
|
||||
@@ -14,6 +14,10 @@ export default class RoleModel extends Model {
|
||||
@attr('string') name;
|
||||
@attr('string') guard_name;
|
||||
@attr('string') description;
|
||||
@attr('string') service;
|
||||
@attr('string') type;
|
||||
@attr('boolean') is_mutable;
|
||||
@attr('boolean') is_deletable;
|
||||
|
||||
/** @dates */
|
||||
@attr('date') created_at;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import Model, { attr } from '@ember-data/model';
|
||||
import Model, { attr, belongsTo, hasMany } from '@ember-data/model';
|
||||
import { computed, get } from '@ember/object';
|
||||
import { not } from '@ember/object/computed';
|
||||
import { getOwner } from '@ember/application';
|
||||
@@ -28,9 +28,13 @@ export default class UserModel extends Model {
|
||||
@attr('string') status;
|
||||
@attr('boolean') is_online;
|
||||
@attr('boolean') is_admin;
|
||||
@attr('raw') types;
|
||||
@attr('raw') meta;
|
||||
|
||||
/** @relationships */
|
||||
@belongsTo('role') role;
|
||||
@hasMany('policy') policies;
|
||||
@hasMany('permission') permissions;
|
||||
|
||||
/** @dates */
|
||||
@attr('date') last_seen_at;
|
||||
@attr('date') phone_verified_at;
|
||||
@@ -43,7 +47,7 @@ export default class UserModel extends Model {
|
||||
/** @methods */
|
||||
deactivate() {
|
||||
const owner = getOwner(this);
|
||||
const fetch = owner.lookup(`service:fetch`);
|
||||
const fetch = owner.lookup('service:fetch');
|
||||
|
||||
return fetch.patch(`users/deactivate/${this.id}`).then((response) => {
|
||||
this.session_status = 'inactive';
|
||||
@@ -54,7 +58,7 @@ export default class UserModel extends Model {
|
||||
|
||||
activate() {
|
||||
const owner = getOwner(this);
|
||||
const fetch = owner.lookup(`service:fetch`);
|
||||
const fetch = owner.lookup('service:fetch');
|
||||
|
||||
return fetch.patch(`users/activate/${this.id}`).then((response) => {
|
||||
this.session_status = 'active';
|
||||
@@ -65,27 +69,72 @@ export default class UserModel extends Model {
|
||||
|
||||
removeFromCurrentCompany() {
|
||||
const owner = getOwner(this);
|
||||
const fetch = owner.lookup(`service:fetch`);
|
||||
const fetch = owner.lookup('service:fetch');
|
||||
|
||||
return fetch.delete(`users/remove-from-company/${this.id}`);
|
||||
}
|
||||
|
||||
resendInvite() {
|
||||
const owner = getOwner(this);
|
||||
const fetch = owner.lookup(`service:fetch`);
|
||||
const fetch = owner.lookup('service:fetch');
|
||||
|
||||
return fetch.post(`users/resend-invite`, { user: this.id });
|
||||
}
|
||||
|
||||
getPermissions() {
|
||||
const permissions = [];
|
||||
|
||||
// get direct applied permissions
|
||||
if (this.get('permissions')) {
|
||||
permissions.pushObjects(this.get('permissions').toArray());
|
||||
}
|
||||
|
||||
// get role permissions and role policies permissions
|
||||
if (this.get('role')) {
|
||||
if (this.get('role.permissions')) {
|
||||
permissions.pushObjects(this.get('role.permissions').toArray());
|
||||
}
|
||||
|
||||
if (this.get('role.policies')) {
|
||||
for (let i = 0; i < this.get('role.policies').length; i++) {
|
||||
const policy = this.get('role.policies').objectAt(i);
|
||||
if (policy.get('permissions')) {
|
||||
permissions.pushObjects(policy.get('permissions').toArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get direct applied policy permissions
|
||||
if (this.get('policies')) {
|
||||
for (let i = 0; i < this.get('policies').length; i++) {
|
||||
const policy = this.get('policies').objectAt(i);
|
||||
if (policy.get('permissions')) {
|
||||
permissions.pushObjects(policy.get('permissions').toArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return permissions;
|
||||
}
|
||||
|
||||
/** @computed */
|
||||
@not('isEmailVerified') emailIsNotVerified;
|
||||
@not('isPhoneVerified') phoneIsNotVerified;
|
||||
|
||||
/** @computed */
|
||||
get allPermissions() {
|
||||
return this.getPermissions();
|
||||
}
|
||||
|
||||
@computed('meta.two_factor_enabled') get isTwoFactorEnabled() {
|
||||
return this.meta && this.meta.two_factor_enabled;
|
||||
}
|
||||
|
||||
@computed('is_admin') get isAdmin() {
|
||||
return this.is_admin === true;
|
||||
}
|
||||
|
||||
@computed('types') get typesList() {
|
||||
const types = Array.from(this.types);
|
||||
return types.join(', ');
|
||||
|
||||
@@ -4,6 +4,12 @@ import { inject as service } from '@ember/service';
|
||||
export default class AuthForgotPasswordRoute extends Route {
|
||||
@service store;
|
||||
|
||||
queryParams = {
|
||||
email: {
|
||||
refreshModel: false,
|
||||
},
|
||||
};
|
||||
|
||||
model() {
|
||||
return this.store.findRecord('brand', 1);
|
||||
}
|
||||
|
||||
@@ -4,41 +4,9 @@ import { action } from '@ember/object';
|
||||
import '@fleetbase/leaflet-routing-machine';
|
||||
|
||||
export default class ConsoleRoute extends Route {
|
||||
/**
|
||||
* Inject the `store` service
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service store;
|
||||
|
||||
/**
|
||||
* Inject the `fetch` service
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service fetch;
|
||||
|
||||
/**
|
||||
* Inject the `session` service
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service session;
|
||||
|
||||
/**
|
||||
* Inject the `intl` service
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service intl;
|
||||
|
||||
/**
|
||||
* Inject the `currentUser` service
|
||||
*
|
||||
* @var {Service}
|
||||
*/
|
||||
@service currentUser;
|
||||
|
||||
/**
|
||||
* Require authentication to access all `console` routes.
|
||||
*
|
||||
@@ -61,54 +29,4 @@ export default class ConsoleRoute extends Route {
|
||||
model() {
|
||||
return this.store.findRecord('brand', 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* We will use this hook to preload engines
|
||||
*
|
||||
* @void
|
||||
*/
|
||||
@action afterModel() {
|
||||
this.fetchSessionInfo();
|
||||
}
|
||||
|
||||
/**
|
||||
* We will use this hook to setup controller and more
|
||||
*
|
||||
* @void
|
||||
*/
|
||||
@action setupController(controller, model) {
|
||||
super.setupController(controller, model);
|
||||
|
||||
// Get and set user locale
|
||||
this.fetch.get('users/locale').then(({ locale }) => {
|
||||
this.intl.setLocale(locale);
|
||||
});
|
||||
|
||||
// Get user organizations
|
||||
this.fetch.get('auth/organizations').then((organizations) => {
|
||||
this.currentUser.setOption('organizations', organizations);
|
||||
controller.organizations = organizations;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this hook to fetch user related queries
|
||||
*
|
||||
* @void
|
||||
*/
|
||||
@action fetchSessionInfo() {
|
||||
this.fetch.shouldResetCache();
|
||||
this.fetch
|
||||
.cachedGet(
|
||||
'lookup/whois',
|
||||
{},
|
||||
{
|
||||
expirationInterval: 60,
|
||||
expirationIntervalUnit: 'minutes',
|
||||
}
|
||||
)
|
||||
.then((whois) => {
|
||||
this.currentUser.setOption('whois', whois);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@ export default class ConsoleAdminRoute extends Route {
|
||||
@service router;
|
||||
|
||||
beforeModel() {
|
||||
// USER MUST BE ADMIN
|
||||
if (!this.currentUser.user.is_admin) {
|
||||
if (!this.currentUser.isAdmin) {
|
||||
return this.router.transitionTo('console').then(() => {
|
||||
this.notifications.error('You do not have authorization to access admin!');
|
||||
});
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
import ApplicationSerializer from '@fleetbase/ember-core/serializers/application';
|
||||
import { EmbeddedRecordsMixin } from '@ember-data/serializer/rest';
|
||||
|
||||
export default class UserSerializer extends ApplicationSerializer.extend(EmbeddedRecordsMixin) {
|
||||
/**
|
||||
* Embedded relationship attributes
|
||||
*
|
||||
* @var {Object}
|
||||
*/
|
||||
get attrs() {
|
||||
return {
|
||||
role: { serialize: 'ids', deserialize: 'records' },
|
||||
policies: { serialize: 'ids', deserialize: 'records' },
|
||||
permissions: { serialize: 'ids', deserialize: 'records' },
|
||||
};
|
||||
}
|
||||
|
||||
export default class UserSerializer extends ApplicationSerializer {
|
||||
/**
|
||||
* Customize serializer so that the password is never sent to the server via Ember Data
|
||||
*
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
{{t "auth.forgot-password.is-sent.message" htmlSafe=true}}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-row mt-4">
|
||||
<Button @icon="check" @type="primary" @text={{t "common.continue"}} @onClick={{transition-to "auth.login"}} />
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="flex px-3 py-2 mb-6 rounded-md shadow-sm bg-blue-200">
|
||||
<div>
|
||||
@@ -31,12 +34,27 @@
|
||||
{{t "auth.forgot-password.form.email-label"}}
|
||||
</label>
|
||||
<div class="mt-2">
|
||||
<Input @value={{this.email}} @type="email" id="email" name="email" required class="form-input form-input-lg w-full" placeholder={{t "auth.forgot-password.form.email-label"}} />
|
||||
<Input
|
||||
@value={{this.email}}
|
||||
@type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
required
|
||||
class="form-input form-input-lg w-full"
|
||||
placeholder={{t "auth.forgot-password.form.email-label"}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-row space-x-2">
|
||||
<Button @icon="magic" @type="primary" @buttonType="submit" @text={{t "auth.forgot-password.form.submit-button"}} @onClick={{this.sendSecureLink}} @isLoading={{this.isLoading}} />
|
||||
<Button
|
||||
@icon="magic"
|
||||
@type="primary"
|
||||
@buttonType="submit"
|
||||
@text={{t "auth.forgot-password.form.submit-button"}}
|
||||
@onClick={{perform this.sendSecureLink}}
|
||||
@isLoading={{this.sendSecureLink.isRunning}}
|
||||
/>
|
||||
<Button @buttonType="button" @text={{t "auth.forgot-password.form.nevermind-button"}} @onClick={{fn (transition-to "auth.login")}} @disabled={{this.isLoading}} />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{{page-title (t "app.name")}}
|
||||
<Layout::Container>
|
||||
<Layout::Header @brand={{@model}} @user={{this.user}} @organizations={{this.organizations}} @menuItems={{this.universe.headerMenuItems}} @extensions={{this.extensions}} @onAction={{this.onAction}} @showSidebarToggle={{true}} @sidebarToggleEnabled={{this.sidebarToggleEnabled}} @onSidebarToggle={{this.onSidebarToggle}} />
|
||||
<Layout::Header @brand={{@model}} @menuItems={{this.menuItems}} @organizationMenuItems={{this.organizationMenuItems}} @userMenuItems={{this.userMenuItems}} @onAction={{this.onAction}} @showSidebarToggle={{true}} @sidebarToggleEnabled={{this.sidebarToggleEnabled}} @onSidebarToggle={{this.onSidebarToggle}} />
|
||||
<Layout::Main>
|
||||
<Layout::Sidebar @onSetup={{this.setSidebarContext}}>
|
||||
<div class="next-sidebar-content-inner">
|
||||
@@ -12,7 +12,11 @@
|
||||
{{outlet}}
|
||||
</Layout::Section>
|
||||
</Layout::Main>
|
||||
<Layout::MobileNavbar @brand={{@model}} @user={{this.user}} @organizations={{this.organizations}} @menuItems={{this.universe.headerMenuItems}} @extensions={{this.extensions}} @onAction={{this.onAction}} />
|
||||
<Layout::MobileNavbar @brand={{@model}} @user={{this.user}} @organizations={{this.organizations}} @menuItems={{this.menuItems}} @extensions={{this.extensions}} @onAction={{this.onAction}} />
|
||||
</Layout::Container>
|
||||
<ChatContainer />
|
||||
<ConsoleWormhole />
|
||||
{{!-- template-lint-disable no-potential-path-strings --}}
|
||||
<RegistryYield @registry="@fleetbase/console" as |RegistryComponent|>
|
||||
<RegistryComponent @controller={{this}} />
|
||||
</RegistryYield>
|
||||
|
||||
@@ -31,8 +31,10 @@
|
||||
<Layout::Sidebar::Item @route="console.admin.config.filesystem" @icon="hard-drive">{{t "common.filesystem"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.queue" @icon="layer-group">{{t "common.queue"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.socket" @icon="plug">{{t "common.socket"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.notification-channels" @icon="tower-broadcast">{{t "common.notification-channels"}}</Layout::Sidebar::Item>
|
||||
<Layout::Sidebar::Item @route="console.admin.config.notification-channels" @icon="tower-broadcast">{{t "common.push-notifications"}}</Layout::Sidebar::Item>
|
||||
</Layout::Sidebar::Panel>
|
||||
</EmberWormhole>
|
||||
|
||||
{{outlet}}
|
||||
<Layout::Section::Container>
|
||||
{{outlet}}
|
||||
</Layout::Section::Container>
|
||||
@@ -1,4 +1,4 @@
|
||||
{{page-title "Notifications"}}
|
||||
{{page-title "Push Notifications"}}
|
||||
<Layout::Section::Header @title={{t "console.admin.notifications.title"}}>
|
||||
<Button @type="primary" @size="sm" @icon="save" @text={{t "common.save-button-text"}} @onClick={{this.saveSettings}} @disabled={{this.isLoading}} @isLoading={{this.isLoading}} />
|
||||
</Layout::Section::Header>
|
||||
@@ -7,9 +7,9 @@
|
||||
<div class="container mx-auto h-screen">
|
||||
<div class="max-w-3xl my-10 mx-auto space-y-4">
|
||||
{{#each-in this.groupedNotifications as |groupName notifications|}}
|
||||
<ContentPanel @title={{concat (smart-humanize groupName) (t "console.admin.notifications.notification-settings") }} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
<ContentPanel @title={{concat (smart-humanize groupName) " " (t "console.admin.notifications.notification-settings") }} @open={{true}} @pad={{true}} @panelBodyClass="bg-white dark:bg-gray-800">
|
||||
{{#each notifications as |notification|}}
|
||||
<InputGroup @name={{notification.name}} @helpText={{notification.description}}>
|
||||
<InputGroup @name={{titleize notification.name}} @helpText={{notification.description}}>
|
||||
<div class="fleetbase-model-select fleetbase-power-select ember-model-select">
|
||||
<PowerSelectMultiple
|
||||
@searchEnabled={{true}}
|
||||
|
||||
@@ -15,9 +15,8 @@
|
||||
@paginationMeta={{@model.meta}}
|
||||
@page={{this.page}}
|
||||
@onPageChange={{fn (mut this.page)}}
|
||||
@tfootVerticalOffset="53"
|
||||
@tfootVerticalOffsetElements=".next-view-section-subheader"
|
||||
@onRowClick={{this.goToCompany}}
|
||||
/>
|
||||
</Layout::Section::Body>
|
||||
|
||||
{{outlet}}
|
||||
@@ -1,5 +1,15 @@
|
||||
{{page-title (t "common.users")}}
|
||||
<Overlay @isOpen={{@isOpen}} @onLoad={{this.setOverlayContext}} @position="right" @noBackdrop={{true}} @fullHeight={{true}} @width="600px" @isResizable={{true}}>
|
||||
<Overlay
|
||||
@onLoad={{this.setOverlayContext}}
|
||||
@onOpen={{this.onOpen}}
|
||||
@onClose={{this.onClose}}
|
||||
@onToggle={{this.onToggle}}
|
||||
@position="right"
|
||||
@noBackdrop={{true}}
|
||||
@fullHeight={{true}}
|
||||
@isResizeble={{true}}
|
||||
@width="800px"
|
||||
>
|
||||
<Overlay::Header @title={{concat this.company.name " - " (t "common.users")}} @hideStatusDot={{true}} @titleWrapperClass="leading-5">
|
||||
<div class="flex flex-1 justify-end">
|
||||
<Button @type="default" @icon="times" @helpText={{t "common.close-and-save"}} @onClick={{this.onPressClose}} />
|
||||
@@ -7,9 +17,17 @@
|
||||
</Overlay::Header>
|
||||
|
||||
<Overlay::Body class="without-padding">
|
||||
{{!-- template-lint-disable no-unbound --}}
|
||||
{{! template-lint-disable no-unbound }}
|
||||
<Layout::Section::Header @title={{t "console.admin.organizations.users.title"}} @searchQuery={{unbound this.nestedQuery}} @onSearch={{this.search}}>
|
||||
<Pagination @meta={{@model.meta}} @page={{this.nestedPage}} @onPageChange={{fn (mut this.nestedPage)}} @metaInfoClass="hidden" @metaInfoWrapperClass="within-layout-section-header" />
|
||||
{{#if (gt @model.meta.total this.nestedLimit)}}
|
||||
<Pagination
|
||||
@meta={{@model.meta}}
|
||||
@page={{this.nestedPage}}
|
||||
@onPageChange={{fn (mut this.nestedPage)}}
|
||||
@metaInfoClass="hidden"
|
||||
@metaInfoWrapperClass="within-layout-section-header"
|
||||
/>
|
||||
{{/if}}
|
||||
</Layout::Section::Header>
|
||||
|
||||
<Layout::Section::Body>
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
{{#if this.ready}}
|
||||
{{mount "@fleetbase/fleetops-engine"}}
|
||||
{{/if}}
|
||||
@@ -13,7 +13,7 @@
|
||||
<FaIcon @icon="info-circle" class="text-blue-900 mr-4" />
|
||||
</div>
|
||||
<p class="flex-1 text-sm text-blue-900 dark:text-blue-900">
|
||||
{{t "invite.invitation-sent-message" htnmlSafe=true companyName=@model.name appName=(t "app.name")}}
|
||||
{{t "invite.for-users.invitation-sent-message" htnmlSafe=true companyName=@model.name appName=(t "app.name")}}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
export function injectAsset ({ type, content, name, syntax }) {
|
||||
switch (type) {
|
||||
case 'css':
|
||||
injectStylesheet(content, syntax);
|
||||
break;
|
||||
case 'meta':
|
||||
injectMeta(name, content);
|
||||
break;
|
||||
case 'js':
|
||||
default:
|
||||
injectScript(content, syntax);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
export function injectMeta (name, content) {
|
||||
const meta = document.createElement('meta');
|
||||
meta.name = name;
|
||||
meta.content = content;
|
||||
document.head.appendChild(meta);
|
||||
}
|
||||
|
||||
export function injectScript (content, type = 'application/javascript') {
|
||||
const script = document.createElement('script');
|
||||
script.type = type;
|
||||
script.text = content;
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
export function injectStylesheet (content, type = 'text/css') {
|
||||
const style = document.createElement('style');
|
||||
style.type = type;
|
||||
if (style.styleSheet) {
|
||||
style.styleSheet.cssText = content;
|
||||
} else {
|
||||
style.appendChild(document.createTextNode(content));
|
||||
}
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
export default {
|
||||
injectAsset,
|
||||
injectMeta,
|
||||
injectScript,
|
||||
injectStylesheet,
|
||||
};
|
||||
@@ -2,6 +2,7 @@
|
||||
const toBoolean = require('./utils/to-boolean');
|
||||
const getenv = require('./utils/getenv');
|
||||
const fixApiHost = require('./utils/fix-api-host');
|
||||
const asArray = require('./utils/as-array');
|
||||
const { version } = require('../package');
|
||||
|
||||
module.exports = function (environment) {
|
||||
@@ -20,7 +21,7 @@ module.exports = function (environment) {
|
||||
},
|
||||
|
||||
APP: {
|
||||
showExtensionsLink: toBoolean(getenv('SHOW_EXTENSIONS_LINK', true)),
|
||||
extensions: asArray(getenv('EXTENSIONS')),
|
||||
},
|
||||
|
||||
API: {
|
||||
@@ -29,8 +30,8 @@ module.exports = function (environment) {
|
||||
},
|
||||
|
||||
osrm: {
|
||||
host: getenv('OSRM_HOST', 'https://bundle.routing.fleetbase.io'),
|
||||
servers: getenv('OSRM_SERVERS', '').split(',').filter(Boolean),
|
||||
host: getenv('OSRM_HOST', 'https://router.project-osrm.org'),
|
||||
servers: {},
|
||||
},
|
||||
|
||||
socket: {
|
||||
@@ -41,7 +42,7 @@ module.exports = function (environment) {
|
||||
},
|
||||
|
||||
stripe: {
|
||||
publishableKey: getenv('STRIPE_KEY')
|
||||
publishableKey: getenv('STRIPE_KEY'),
|
||||
},
|
||||
|
||||
defaultValues: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
module.exports = function () {
|
||||
return {
|
||||
'free-solid-svg-icons': 'all',
|
||||
'free-brands-svg-icons': 'all',
|
||||
};
|
||||
};
|
||||
|
||||
11
console/config/utils/as-array.js
Normal file
11
console/config/utils/as-array.js
Normal file
@@ -0,0 +1,11 @@
|
||||
module.exports = function asArray(value) {
|
||||
if (Array.isArray(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (typeof value === 'string' && value.includes(',')) {
|
||||
return value.split(',');
|
||||
}
|
||||
|
||||
return [];
|
||||
};
|
||||
@@ -4,5 +4,4 @@ SOCKETCLUSTER_PATH=/socketcluster/
|
||||
SOCKETCLUSTER_HOST=localhost
|
||||
SOCKETCLUSTER_SECURE=false
|
||||
SOCKETCLUSTER_PORT=38000
|
||||
OSRM_HOST=https://bundle.routing.fleetbase.io
|
||||
OSRM_SERVERS=https://canada.routing.fleetbase.io,https://us.routing.fleetbase.io
|
||||
OSRM_HOST=https://router.project-osrm.org
|
||||
@@ -5,5 +5,4 @@ SOCKETCLUSTER_PATH=/socketcluster/
|
||||
SOCKETCLUSTER_HOST=
|
||||
SOCKETCLUSTER_SECURE=true
|
||||
SOCKETCLUSTER_PORT=38000
|
||||
OSRM_HOST=https://bundle.routing.fleetbase.io
|
||||
OSRM_SERVERS=https://canada.routing.fleetbase.io,https://us.routing.fleetbase.io
|
||||
OSRM_HOST=https://router.project-osrm.org
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@fleetbase/console",
|
||||
"version": "0.4.27",
|
||||
"version": "0.5.7",
|
||||
"private": true,
|
||||
"description": "Modular logistics and supply chain operating system (LSOS)",
|
||||
"repository": "https://github.com/fleetbase/fleetbase",
|
||||
@@ -29,57 +29,58 @@
|
||||
"test:ember": "ember test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fleetbase/ember-core": "^0.2.12",
|
||||
"@fleetbase/ember-ui": "^0.2.18",
|
||||
"@fleetbase/fleetops-engine": "^0.5.3",
|
||||
"@fleetbase/storefront-engine": "^0.3.12",
|
||||
"@fleetbase/dev-engine": "^0.2.4",
|
||||
"@fleetbase/iam-engine": "^0.0.13",
|
||||
"@fleetbase/ember-core": "^0.2.17",
|
||||
"@fleetbase/ember-ui": "^0.2.24",
|
||||
"@fleetbase/fleetops-engine": "^0.5.7",
|
||||
"@fleetbase/storefront-engine": "^0.3.14",
|
||||
"@fleetbase/dev-engine": "^0.2.6",
|
||||
"@fleetbase/iam-engine": "^0.1.0",
|
||||
"@fleetbase/registry-bridge-engine": "^0.0.13",
|
||||
"@fleetbase/fleetops-data": "^0.1.17",
|
||||
"@fleetbase/leaflet-routing-machine": "^3.2.16",
|
||||
"@ember/legacy-built-in-components": "^0.4.1",
|
||||
"@fortawesome/ember-fontawesome": "^0.4.1",
|
||||
"@ember/legacy-built-in-components": "^0.4.2",
|
||||
"@fortawesome/ember-fontawesome": "^2.0.0",
|
||||
"ember-changeset": "^4.1.2",
|
||||
"ember-changeset-validations": "^4.1.1",
|
||||
"ember-composable-helpers": "^5.0.0",
|
||||
"ember-concurrency": "^3.0.0",
|
||||
"ember-concurrency": "^3.1.1",
|
||||
"ember-concurrency-decorators": "^2.0.3",
|
||||
"ember-gridstack": "^4.0.0",
|
||||
"ember-intl": "6.3.2",
|
||||
"ember-math-helpers": "^2.18.2",
|
||||
"ember-power-select": "^6.0.1",
|
||||
"ember-power-select": "^7.2.0",
|
||||
"ember-prism": "^0.13.0",
|
||||
"ember-radio-button": "3.0.0-beta.1",
|
||||
"ember-tag-input": "^3.1.0",
|
||||
"fleetbase-extensions-indexer": "^0.0.5",
|
||||
"gridstack": "^7.2.2",
|
||||
"gridstack": "^7.3.0",
|
||||
"patch-package": "^8.0.0",
|
||||
"postcss-at-rules-variables": "^0.3.0",
|
||||
"postcss-custom-properties": "^12.1.9",
|
||||
"postcss-custom-properties": "^12.1.11",
|
||||
"postcss-nth-list": "^1.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@fleetbase/intl-lint": "^0.0.1",
|
||||
"@babel/core": "^7.23.2",
|
||||
"@babel/eslint-parser": "^7.22.15",
|
||||
"@babel/plugin-proposal-decorators": "^7.23.2",
|
||||
"@ember/optional-features": "^2.0.0",
|
||||
"@babel/core": "^7.25.2",
|
||||
"@babel/eslint-parser": "^7.25.1",
|
||||
"@babel/plugin-proposal-decorators": "^7.24.7",
|
||||
"@ember/optional-features": "^2.1.0",
|
||||
"@ember/string": "^3.1.1",
|
||||
"@ember/test-helpers": "^3.2.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||
"@ember/test-helpers": "^3.3.1",
|
||||
"@fleetbase/intl-lint": "^0.0.1",
|
||||
"@fortawesome/fontawesome-svg-core": "6.4.0",
|
||||
"@fortawesome/free-brands-svg-icons": "6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "6.4.0",
|
||||
"@glimmer/component": "^1.1.2",
|
||||
"@glimmer/tracking": "^1.1.2",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"autoprefixer": "^10.4.8",
|
||||
"@tailwindcss/forms": "^0.5.7",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"broccoli-asset-rev": "^3.0.0",
|
||||
"broccoli-funnel": "^3.0.8",
|
||||
"concurrently": "^8.2.2",
|
||||
"date-fns": "^2.30.0",
|
||||
"dragula": "^3.7.3",
|
||||
"ember-auto-import": "^2.6.3",
|
||||
"ember-cli": "~5.4.1",
|
||||
"ember-auto-import": "^2.7.4",
|
||||
"ember-cli": "~5.4.2",
|
||||
"ember-cli-app-version": "^6.0.1",
|
||||
"ember-cli-babel": "^8.2.0",
|
||||
"ember-cli-clean-css": "^3.0.0",
|
||||
@@ -91,48 +92,48 @@
|
||||
"ember-cli-sri": "^2.1.1",
|
||||
"ember-cli-string-helpers": "^6.1.0",
|
||||
"ember-cli-terser": "^4.0.2",
|
||||
"ember-data": "^4.12.5",
|
||||
"ember-engines": "^0.8.23",
|
||||
"ember-data": "^4.12.8",
|
||||
"ember-engines": "^0.9.0",
|
||||
"ember-fetch": "^8.1.2",
|
||||
"ember-leaflet": "^5.1.1",
|
||||
"ember-leaflet": "^5.1.3",
|
||||
"ember-load-initializers": "^2.1.2",
|
||||
"ember-modifier": "^4.1.0",
|
||||
"ember-page-title": "^8.0.0",
|
||||
"ember-qunit": "^8.0.1",
|
||||
"ember-modifier": "^4.2.0",
|
||||
"ember-page-title": "^8.2.3",
|
||||
"ember-qunit": "^8.1.0",
|
||||
"ember-resolver": "^11.0.1",
|
||||
"ember-responsive": "^5.0.0",
|
||||
"ember-source": "~5.4.0",
|
||||
"ember-template-lint": "^5.11.2",
|
||||
"ember-source": "~5.4.1",
|
||||
"ember-template-lint": "^5.13.0",
|
||||
"ember-wormhole": "^0.6.0",
|
||||
"eslint": "^8.52.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-ember": "^11.11.1",
|
||||
"eslint-plugin-n": "^16.2.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"eslint-plugin-qunit": "^8.0.1",
|
||||
"fast-glob": "^3.3.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-ember": "^11.12.0",
|
||||
"eslint-plugin-n": "^16.6.2",
|
||||
"eslint-plugin-prettier": "^5.2.1",
|
||||
"eslint-plugin-qunit": "^8.1.2",
|
||||
"fast-glob": "^3.3.2",
|
||||
"fs": "0.0.1-security",
|
||||
"inter-ui": "^3.19.3",
|
||||
"leaflet": "^1.9.4",
|
||||
"loader.js": "^4.7.0",
|
||||
"normalize.css": "^8.0.1",
|
||||
"postcss": "^8.4.21",
|
||||
"postcss": "^8.4.41",
|
||||
"postcss-conditionals-renewed": "^1.0.0",
|
||||
"postcss-each": "^1.1.0",
|
||||
"postcss-import": "14.1.0",
|
||||
"postcss-mixins": "^9.0.4",
|
||||
"postcss-preset-env": "^7.8.2",
|
||||
"postcss-simple-vars": "^7.0.0",
|
||||
"prettier": "^3.0.3",
|
||||
"qunit": "^2.20.0",
|
||||
"postcss-preset-env": "^7.8.3",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"prettier": "^3.3.3",
|
||||
"qunit": "^2.22.0",
|
||||
"qunit-dom": "^2.0.0",
|
||||
"recast": "^0.23.3",
|
||||
"recast": "^0.23.9",
|
||||
"stylelint": "^15.11.0",
|
||||
"stylelint-config-standard": "^34.0.0",
|
||||
"stylelint-prettier": "^4.0.2",
|
||||
"tailwindcss": "^3.1.8",
|
||||
"stylelint-prettier": "^4.1.0",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"tracked-built-ins": "^3.3.0",
|
||||
"webpack": "^5.89.0"
|
||||
"webpack": "^5.94.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
@@ -142,8 +143,8 @@
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"@fleetbase/ember-core": "^0.2.12",
|
||||
"@fleetbase/ember-ui": "^0.2.18",
|
||||
"@fleetbase/ember-core": "^0.2.17",
|
||||
"@fleetbase/ember-ui": "^0.2.24",
|
||||
"@fleetbase/fleetops-data": "^0.1.17"
|
||||
}
|
||||
},
|
||||
|
||||
25439
console/pnpm-lock.yaml
generated
25439
console/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -39,7 +39,7 @@ Router.map(function () {
|
||||
this.route('cache');
|
||||
this.route('filesystem');
|
||||
this.route('mail');
|
||||
this.route('notification-channels');
|
||||
this.route('notification-channels', { path: '/push-notifications' });
|
||||
this.route('queue');
|
||||
this.route('services');
|
||||
this.route('socket');
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from '@fleetbase/console/tests/helpers';
|
||||
import { render } from '@ember/test-helpers';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
|
||||
module('Integration | Component | extension-injector', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
|
||||
test('it renders', async function (assert) {
|
||||
// Set any properties with this.set('myProperty', 'value');
|
||||
// Handle any actions with this.set('myAction', function(val) { ... });
|
||||
|
||||
await render(hbs`<ExtensionInjector />`);
|
||||
|
||||
assert.dom().hasText('');
|
||||
|
||||
// Template block usage:
|
||||
await render(hbs`
|
||||
<ExtensionInjector>
|
||||
template block text
|
||||
</ExtensionInjector>
|
||||
`);
|
||||
|
||||
assert.dom().hasText('template block text');
|
||||
});
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
import Application from '@ember/application';
|
||||
|
||||
import config from '@fleetbase/console/config/environment';
|
||||
import { initialize } from '@fleetbase/console/instance-initializers/register-app-version';
|
||||
import { module, test } from 'qunit';
|
||||
import Resolver from 'ember-resolver';
|
||||
import { run } from '@ember/runloop';
|
||||
|
||||
module('Unit | Instance Initializer | register-app-version', function (hooks) {
|
||||
hooks.beforeEach(function () {
|
||||
this.TestApplication = class TestApplication extends Application {
|
||||
modulePrefix = config.modulePrefix;
|
||||
podModulePrefix = config.podModulePrefix;
|
||||
Resolver = Resolver;
|
||||
};
|
||||
|
||||
this.TestApplication.instanceInitializer({
|
||||
name: 'initializer under test',
|
||||
initialize,
|
||||
});
|
||||
|
||||
this.application = this.TestApplication.create({
|
||||
autoboot: false,
|
||||
});
|
||||
|
||||
this.instance = this.application.buildInstance();
|
||||
});
|
||||
hooks.afterEach(function () {
|
||||
run(this.instance, 'destroy');
|
||||
run(this.application, 'destroy');
|
||||
});
|
||||
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it works', async function (assert) {
|
||||
await this.instance.boot();
|
||||
|
||||
assert.ok(true);
|
||||
});
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
import assetInjector from '@fleetbase/console/utils/asset-injector';
|
||||
import { module, test } from 'qunit';
|
||||
|
||||
module('Unit | Utility | asset-injector', function () {
|
||||
// TODO: Replace this with your real tests.
|
||||
test('it works', function (assert) {
|
||||
let result = assetInjector();
|
||||
assert.ok(result);
|
||||
});
|
||||
});
|
||||
@@ -68,6 +68,12 @@ common:
|
||||
export: Export
|
||||
reload: Reload
|
||||
reload-data: Reload data
|
||||
unauthorized: Unauthorized
|
||||
unauthorized-to: Unauthorized to
|
||||
unauthorized-access: Unauthorized Access
|
||||
unauthorized-access-message: Unauthorized Access, you must request permissions to access.
|
||||
permissions-required-for-changes: You do not have the required permissions to make changes.
|
||||
push-notifications: Push Notifications
|
||||
component:
|
||||
file:
|
||||
dropdown-label: File actions
|
||||
@@ -179,6 +185,7 @@ auth:
|
||||
no-identity-notification: Did you forget to enter your email?
|
||||
no-password-notification: Did you forget to enter your password?
|
||||
unverified-notification: Your account needs to be verified to proceed.
|
||||
password-reset-required: A password reset is required to continue.
|
||||
failed-attempt:
|
||||
message: <strong>Forgot your password?</strong><br> Click the button below to reset your password.
|
||||
button-text: Ok, help me reset!
|
||||
@@ -246,7 +253,7 @@ console:
|
||||
mail:
|
||||
title: Mail Configuration
|
||||
notification-channels:
|
||||
title: Notification Channels Configuration
|
||||
title: Push Notifications Configuration
|
||||
queue:
|
||||
title: Queue Configuration
|
||||
services:
|
||||
|
||||
224
database.mmd
224
database.mmd
@@ -825,88 +825,6 @@ erDiagram
|
||||
TIMESTAMP deleted_at
|
||||
}
|
||||
|
||||
fleetbase_billing_customers {
|
||||
BIGINTUNSIGNED id PK
|
||||
CHAR uuid
|
||||
CHAR company_uuid FK
|
||||
VARCHAR payment_gateway_id
|
||||
VARCHAR pm_type
|
||||
VARCHAR pm_last_four
|
||||
TIMESTAMP trial_ends_at
|
||||
JSON options
|
||||
TIMESTAMP deleted_at
|
||||
TIMESTAMP created_at
|
||||
TIMESTAMP updated_at
|
||||
}
|
||||
|
||||
fleetbase_billing_payment_gateways {
|
||||
BIGINTUNSIGNED id PK
|
||||
CHAR uuid
|
||||
VARCHAR name
|
||||
VARCHAR code
|
||||
VARCHAR description
|
||||
VARCHAR api_key
|
||||
VARCHAR api_secret
|
||||
VARCHAR callback_url
|
||||
VARCHAR return_url
|
||||
VARCHAR webhook_secret
|
||||
CHAR logo_uuid FK
|
||||
CHAR backdrop_uuid FK
|
||||
JSON options
|
||||
TIMESTAMP deleted_at
|
||||
TIMESTAMP created_at
|
||||
TIMESTAMP updated_at
|
||||
}
|
||||
|
||||
fleetbase_billing_plans {
|
||||
BIGINTUNSIGNED id PK
|
||||
CHAR uuid
|
||||
CHAR payment_gateway_uuid FK
|
||||
VARCHAR name
|
||||
TEXT description
|
||||
VARCHAR payment_gateway_id
|
||||
INT price
|
||||
BIT recurring
|
||||
VARCHAR billing_period
|
||||
VARCHAR interval
|
||||
INT trial_period_days
|
||||
JSON options
|
||||
TIMESTAMP deleted_at
|
||||
TIMESTAMP created_at
|
||||
TIMESTAMP updated_at
|
||||
}
|
||||
|
||||
fleetbase_billing_subscription_items {
|
||||
BIGINTUNSIGNED id PK
|
||||
CHAR uuid
|
||||
CHAR subscription_uuid FK
|
||||
VARCHAR payment_gateway_id
|
||||
VARCHAR payment_gateway_product
|
||||
VARCHAR payment_gateway_price
|
||||
INT quantity
|
||||
TIMESTAMP deleted_at
|
||||
TIMESTAMP created_at
|
||||
TIMESTAMP updated_at
|
||||
}
|
||||
|
||||
fleetbase_billing_subscriptions {
|
||||
BIGINTUNSIGNED id PK
|
||||
CHAR uuid
|
||||
CHAR company_uuid FK
|
||||
CHAR payment_gateway_uuid FK
|
||||
CHAR plan_uuid FK
|
||||
VARCHAR type
|
||||
VARCHAR payment_gateway_id
|
||||
VARCHAR payment_gateway_status
|
||||
VARCHAR payment_gateway_price
|
||||
INT quantity
|
||||
TIMESTAMP trial_ends_at
|
||||
TIMESTAMP ends_at
|
||||
TIMESTAMP deleted_at
|
||||
TIMESTAMP created_at
|
||||
TIMESTAMP updated_at
|
||||
}
|
||||
|
||||
fleetbase_categories {
|
||||
INTUNSIGNED id PK
|
||||
VARCHAR _key
|
||||
@@ -1599,6 +1517,7 @@ erDiagram
|
||||
CHAR purchase_rate_uuid FK
|
||||
CHAR tracking_number_uuid FK
|
||||
CHAR driver_assigned_uuid FK
|
||||
CHAR vehicle_assigned_uuid FK
|
||||
JSON meta
|
||||
JSON options
|
||||
BIT dispatched
|
||||
@@ -1764,6 +1683,121 @@ erDiagram
|
||||
TIMESTAMP updated_at
|
||||
}
|
||||
|
||||
fleetbase_registry_extension_bundles {
|
||||
INTUNSIGNED id PK
|
||||
CHAR uuid
|
||||
CHAR public_id
|
||||
CHAR bundle_id
|
||||
CHAR company_uuid FK
|
||||
CHAR created_by_uuid FK
|
||||
CHAR extension_uuid FK
|
||||
CHAR bundle_uuid FK
|
||||
VARCHAR bundle_number
|
||||
VARCHAR version
|
||||
VARCHAR status
|
||||
JSON meta
|
||||
TIMESTAMP created_at
|
||||
TIMESTAMP updated_at
|
||||
TIMESTAMP deleted_at
|
||||
}
|
||||
|
||||
fleetbase_registry_extension_installs {
|
||||
INTUNSIGNED id PK
|
||||
CHAR uuid
|
||||
CHAR company_uuid FK
|
||||
CHAR extension_uuid FK
|
||||
JSON meta
|
||||
TIMESTAMP created_at
|
||||
TIMESTAMP updated_at
|
||||
TIMESTAMP deleted_at
|
||||
}
|
||||
|
||||
fleetbase_registry_extension_purchases {
|
||||
INTUNSIGNED id PK
|
||||
CHAR uuid
|
||||
CHAR company_uuid FK
|
||||
CHAR extension_uuid FK
|
||||
VARCHAR stripe_checkout_session_id
|
||||
VARCHAR stripe_payment_intent_id
|
||||
BIT is_subcription
|
||||
INT locked_price
|
||||
VARCHAR subscription_billing_period
|
||||
VARCHAR subscription_model
|
||||
JSON meta
|
||||
TIMESTAMP created_at
|
||||
TIMESTAMP updated_at
|
||||
TIMESTAMP deleted_at
|
||||
}
|
||||
|
||||
fleetbase_registry_extensions {
|
||||
INTUNSIGNED id PK
|
||||
CHAR uuid
|
||||
CHAR company_uuid FK
|
||||
CHAR created_by_uuid FK
|
||||
CHAR registry_user_uuid FK
|
||||
CHAR current_bundle_uuid FK
|
||||
CHAR next_bundle_uuid FK
|
||||
CHAR icon_uuid FK
|
||||
CHAR category_uuid FK
|
||||
VARCHAR public_id
|
||||
VARCHAR stripe_product_id
|
||||
VARCHAR name
|
||||
VARCHAR subtitle
|
||||
BIT self_managed
|
||||
BIT payment_required
|
||||
INT price
|
||||
INT sale_price
|
||||
BIT on_sale
|
||||
BIT subscription_required
|
||||
VARCHAR subscription_billing_period
|
||||
VARCHAR subscription_model
|
||||
INT subscription_amount
|
||||
JSON subscription_tiers
|
||||
VARCHAR currency
|
||||
VARCHAR slug
|
||||
VARCHAR version
|
||||
VARCHAR fa_icon
|
||||
MEDIUMTEXT description
|
||||
MEDIUMTEXT promotional_text
|
||||
VARCHAR website_url
|
||||
VARCHAR repo_url
|
||||
VARCHAR support_url
|
||||
VARCHAR privacy_policy_url
|
||||
VARCHAR tos_url
|
||||
VARCHAR copyright
|
||||
VARCHAR primary_language
|
||||
JSON tags
|
||||
JSON languages
|
||||
JSON meta
|
||||
BIT core_extension
|
||||
VARCHAR status
|
||||
TIMESTAMP published_at
|
||||
TIMESTAMP accepted_at
|
||||
TIMESTAMP rejected_at
|
||||
TIMESTAMP created_at
|
||||
TIMESTAMP updated_at
|
||||
TIMESTAMP deleted_at
|
||||
}
|
||||
|
||||
fleetbase_registry_users {
|
||||
INTUNSIGNED id PK
|
||||
CHAR uuid
|
||||
CHAR company_uuid FK
|
||||
CHAR user_uuid FK
|
||||
VARCHAR public_id
|
||||
VARCHAR token
|
||||
VARCHAR registry_token
|
||||
VARCHAR scope
|
||||
TIMESTAMP expires_at
|
||||
TIMESTAMP last_used_at
|
||||
VARCHAR name
|
||||
JSON meta
|
||||
BIT revoked
|
||||
TIMESTAMP created_at
|
||||
TIMESTAMP updated_at
|
||||
TIMESTAMP deleted_at
|
||||
}
|
||||
|
||||
fleetbase_role_has_permissions {
|
||||
CHAR permission_id PK
|
||||
CHAR role_id PK
|
||||
@@ -2195,6 +2229,8 @@ erDiagram
|
||||
CHAR place_uuid FK
|
||||
CHAR payload_uuid FK
|
||||
CHAR tracking_number_uuid FK
|
||||
CHAR customer_uuid
|
||||
VARCHAR customer_type
|
||||
VARCHAR _import_id
|
||||
VARCHAR type
|
||||
INT order
|
||||
@@ -2701,13 +2737,10 @@ erDiagram
|
||||
fleetbase_api_credentials ||--o{ fleetbase_webhook_endpoints : "foreign key"
|
||||
fleetbase_api_credentials ||--o{ fleetbase_webhook_request_logs : "foreign key"
|
||||
fleetbase_api_events ||--o{ fleetbase_webhook_request_logs : "foreign key"
|
||||
fleetbase_billing_payment_gateways ||--o{ fleetbase_billing_plans : "foreign key"
|
||||
fleetbase_billing_payment_gateways ||--o{ fleetbase_billing_subscriptions : "foreign key"
|
||||
fleetbase_billing_plans ||--o{ fleetbase_billing_subscriptions : "foreign key"
|
||||
fleetbase_billing_subscriptions ||--o{ fleetbase_billing_subscription_items : "foreign key"
|
||||
fleetbase_categories ||--o{ fleetbase_entities : "foreign key"
|
||||
fleetbase_categories ||--o{ fleetbase_extensions : "foreign key"
|
||||
fleetbase_categories ||--o{ fleetbase_order_configs : "foreign key"
|
||||
fleetbase_categories ||--o{ fleetbase_registry_extensions : "foreign key"
|
||||
fleetbase_categories ||--o{ fleetbase_storefront_network_stores : "foreign key"
|
||||
fleetbase_categories ||--o{ fleetbase_storefront_product_addon_categories : "foreign key"
|
||||
fleetbase_categories ||--o{ fleetbase_storefront_product_addons : "foreign key"
|
||||
@@ -2725,8 +2758,6 @@ erDiagram
|
||||
fleetbase_comments ||--o{ fleetbase_comments : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_api_credentials : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_api_request_logs : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_billing_customers : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_billing_subscriptions : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_chat_attachments : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_chat_channels : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_chat_logs : "foreign key"
|
||||
@@ -2756,6 +2787,11 @@ erDiagram
|
||||
fleetbase_companies ||--o{ fleetbase_positions : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_proofs : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_purchase_rates : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_registry_extension_bundles : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_registry_extension_installs : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_registry_extension_purchases : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_registry_extensions : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_registry_users : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_roles : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_routes : "foreign key"
|
||||
fleetbase_companies ||--o{ fleetbase_service_areas : "foreign key"
|
||||
@@ -2790,7 +2826,6 @@ erDiagram
|
||||
fleetbase_drivers ||--o{ fleetbase_issues : "foreign key"
|
||||
fleetbase_drivers ||--o{ fleetbase_orders : "foreign key"
|
||||
fleetbase_extensions ||--o{ fleetbase_extension_installs : "foreign key"
|
||||
fleetbase_files ||--o{ fleetbase_billing_payment_gateways : "foreign key"
|
||||
fleetbase_files ||--o{ fleetbase_categories : "foreign key"
|
||||
fleetbase_files ||--o{ fleetbase_chat_attachments : "foreign key"
|
||||
fleetbase_files ||--o{ fleetbase_companies : "foreign key"
|
||||
@@ -2798,6 +2833,8 @@ erDiagram
|
||||
fleetbase_files ||--o{ fleetbase_extensions : "foreign key"
|
||||
fleetbase_files ||--o{ fleetbase_order_configs : "foreign key"
|
||||
fleetbase_files ||--o{ fleetbase_proofs : "foreign key"
|
||||
fleetbase_files ||--o{ fleetbase_registry_extension_bundles : "foreign key"
|
||||
fleetbase_files ||--o{ fleetbase_registry_extensions : "foreign key"
|
||||
fleetbase_files ||--o{ fleetbase_users : "foreign key"
|
||||
fleetbase_files ||--o{ fleetbase_vehicles : "foreign key"
|
||||
fleetbase_files ||--o{ fleetbase_vendors : "foreign key"
|
||||
@@ -2834,6 +2871,11 @@ erDiagram
|
||||
fleetbase_places ||--o{ fleetbase_storefront_store_locations : "foreign key"
|
||||
fleetbase_policies ||--o{ fleetbase_model_has_policies : "foreign key"
|
||||
fleetbase_purchase_rates ||--o{ fleetbase_orders : "foreign key"
|
||||
fleetbase_registry_extension_bundles ||--o{ fleetbase_registry_extensions : "foreign key"
|
||||
fleetbase_registry_extensions ||--o{ fleetbase_registry_extension_bundles : "foreign key"
|
||||
fleetbase_registry_extensions ||--o{ fleetbase_registry_extension_installs : "foreign key"
|
||||
fleetbase_registry_extensions ||--o{ fleetbase_registry_extension_purchases : "foreign key"
|
||||
fleetbase_registry_users ||--o{ fleetbase_registry_extensions : "foreign key"
|
||||
fleetbase_roles ||--o{ fleetbase_model_has_roles : "foreign key"
|
||||
fleetbase_roles ||--o{ fleetbase_role_has_permissions : "foreign key"
|
||||
fleetbase_routes ||--o{ fleetbase_orders : "foreign key"
|
||||
@@ -2871,6 +2913,9 @@ erDiagram
|
||||
fleetbase_users ||--o{ fleetbase_issues : "foreign key"
|
||||
fleetbase_users ||--o{ fleetbase_order_configs : "foreign key"
|
||||
fleetbase_users ||--o{ fleetbase_orders : "foreign key"
|
||||
fleetbase_users ||--o{ fleetbase_registry_extension_bundles : "foreign key"
|
||||
fleetbase_users ||--o{ fleetbase_registry_extensions : "foreign key"
|
||||
fleetbase_users ||--o{ fleetbase_registry_users : "foreign key"
|
||||
fleetbase_users ||--o{ fleetbase_user_devices : "foreign key"
|
||||
fleetbase_users ||--o{ fleetbase_webhook_endpoints : "foreign key"
|
||||
fleetbase_users ||--o{ fleetbase_storefront_carts : "foreign key"
|
||||
@@ -2888,6 +2933,7 @@ erDiagram
|
||||
fleetbase_vehicles ||--o{ fleetbase_fleet_vehicles : "foreign key"
|
||||
fleetbase_vehicles ||--o{ fleetbase_fuel_reports : "foreign key"
|
||||
fleetbase_vehicles ||--o{ fleetbase_issues : "foreign key"
|
||||
fleetbase_vehicles ||--o{ fleetbase_orders : "foreign key"
|
||||
fleetbase_vehicles ||--o{ fleetbase_vehicle_devices : "foreign key"
|
||||
fleetbase_vendors ||--o{ fleetbase_drivers : "foreign key"
|
||||
fleetbase_vendors ||--o{ fleetbase_entities : "foreign key"
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
cache:
|
||||
image: redis:4-alpine
|
||||
@@ -38,9 +36,18 @@ services:
|
||||
CACHE_URL: tcp://cache
|
||||
REDIS_URL: tcp://cache
|
||||
|
||||
application:
|
||||
console:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: console/Dockerfile.server-build
|
||||
args:
|
||||
ENVIRONMENT: development
|
||||
ports:
|
||||
- "4201:4201"
|
||||
- "4200:4200"
|
||||
volumes:
|
||||
- console-build:/console
|
||||
|
||||
application:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile
|
||||
@@ -48,7 +55,10 @@ services:
|
||||
args:
|
||||
ENVIRONMENT: development
|
||||
GITHUB_AUTH_KEY: ${GITHUB_AUTH_KEY}
|
||||
volumes:
|
||||
- console-build:/fleetbase/console
|
||||
environment:
|
||||
ENVIRONMENT: development
|
||||
DATABASE_URL: "mysql://root@database/fleetbase"
|
||||
QUEUE_CONNECTION: redis
|
||||
CACHE_DRIVER: redis
|
||||
@@ -63,6 +73,9 @@ services:
|
||||
MODEL_CACHE_ENABLED: 'true'
|
||||
RESPONSE_CACHE_ENABLED: 'true'
|
||||
RESPONSE_CACHE_DRIVER: redis
|
||||
REGISTRY_HOST: https://registry.fleetbase.io
|
||||
REGISTRY_PREINSTALLED_EXTENSIONS: 'true'
|
||||
OSRM_HOST: https://router.project-osrm.org
|
||||
depends_on:
|
||||
- database
|
||||
- cache
|
||||
@@ -76,3 +89,6 @@ services:
|
||||
- "8000:80"
|
||||
depends_on:
|
||||
- application
|
||||
|
||||
volumes:
|
||||
console-build:
|
||||
@@ -1,6 +1,6 @@
|
||||
# syntax = docker/dockerfile:1.2
|
||||
# Base stage
|
||||
FROM dunglas/frankenphp:1.1.0-php8.2-bookworm as base
|
||||
FROM dunglas/frankenphp:1.2.3-php8.2-bookworm as base
|
||||
|
||||
# Install packages
|
||||
RUN apt-get update && apt-get install -y git bind9-utils mycli nodejs npm nano \
|
||||
@@ -31,17 +31,17 @@ RUN sed -e 's/^expose_php.*/expose_php = Off/' "$PHP_INI_DIR/php.ini-production"
|
||||
-e 's/^memory_limit.*/memory_limit = 600M/' "$PHP_INI_DIR/php.ini"
|
||||
|
||||
# Install global node modules
|
||||
RUN npm install -g chokidar pnpm ember-cli
|
||||
RUN npm install -g chokidar pnpm ember-cli npm-cli-login
|
||||
|
||||
# Install ssm-parent
|
||||
COPY --from=ghcr.io/springload/ssm-parent:1.8 /usr/bin/ssm-parent /sbin/ssm-parent
|
||||
|
||||
# # Create the pnpm directory and set the PNPM_HOME environment variable
|
||||
# RUN mkdir -p ~/.pnpm
|
||||
# ENV PNPM_HOME /root/.pnpm
|
||||
# Create the pnpm directory and set the PNPM_HOME environment variable
|
||||
RUN mkdir -p ~/.pnpm
|
||||
ENV PNPM_HOME /root/.pnpm
|
||||
|
||||
# # Add the pnpm global bin to the PATH
|
||||
# ENV PATH /root/.pnpm/bin:$PATH
|
||||
# Add the pnpm global bin to the PATH
|
||||
ENV PATH /root/.pnpm/bin:$PATH
|
||||
|
||||
# Set some build ENV variables
|
||||
ENV LOG_CHANNEL=stdout
|
||||
@@ -63,33 +63,7 @@ ARG GITHUB_AUTH_KEY
|
||||
COPY --chown=www-data:www-data ./Caddyfile $CADDYFILE_PATH
|
||||
|
||||
# Create /fleetbase directory and set correct permissions
|
||||
RUN mkdir -p /fleetbase/api && chown -R www-data:www-data /fleetbase
|
||||
|
||||
## -- Start Console Setup --
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /fleetbase/console
|
||||
|
||||
# TEMPORARILY ADD REGISTRY BRIDGE AND CORE API
|
||||
COPY ./packages/registry-bridge /fleetbase/packages/registry-bridge
|
||||
COPY ./packages/core-api /fleetbase/packages/core-api
|
||||
|
||||
# Copy pnpm-lock.yaml (or package.json) into the directory /app in the container
|
||||
COPY ./console/package.json ./console/pnpm-lock.yaml /fleetbase/console/
|
||||
|
||||
# Copy over .npmrc if applicable
|
||||
COPY ./console/.npmr[c] /fleetbase/console/
|
||||
|
||||
# Install app dependencies
|
||||
# RUN pnpm install
|
||||
|
||||
# Copy the console directory contents into the container at /app
|
||||
COPY ./console /fleetbase/console/
|
||||
|
||||
# Build the application
|
||||
# RUN pnpm build --environment $ENVIRONMENT
|
||||
|
||||
## -- End Console Setup --
|
||||
RUN mkdir -p /fleetbase/api && mkdir -p /fleetbase/console && chown -R www-data:www-data /fleetbase
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /fleetbase/api
|
||||
@@ -156,9 +130,9 @@ CMD ["php", "artisan", "queue:work"]
|
||||
FROM base as app-dev
|
||||
ENTRYPOINT ["docker-php-entrypoint"]
|
||||
# Add --watch flag later
|
||||
CMD ["sh", "-c", "php artisan octane:frankenphp --port=8000 --host=0.0.0.0 --caddyfile $CADDYFILE_PATH"]
|
||||
CMD ["sh", "-c", "php artisan octane:frankenphp --workers=6 --max-requests=250 --port=8000 --host=0.0.0.0 --caddyfile $CADDYFILE_PATH"]
|
||||
|
||||
# Application stage
|
||||
FROM base as app
|
||||
ENTRYPOINT ["/sbin/ssm-parent", "-c", ".ssm-parent.yaml", "run", "--", "docker-php-entrypoint"]
|
||||
CMD ["sh", "-c", "php artisan octane:frankenphp --port=8000 --host=0.0.0.0 --https --http-redirect --caddyfile $CADDYFILE_PATH"]
|
||||
CMD ["sh", "-c", "php artisan octane:frankenphp --workers=6 --max-requests=250 --port=8000 --host=0.0.0.0 --https --http-redirect --caddyfile $CADDYFILE_PATH"]
|
||||
|
||||
2
docs
2
docs
Submodule docs updated: 6e1281f528...61704f4855
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 2.3 MiB After Width: | Height: | Size: 2.3 MiB |
20714
erd.svg
20714
erd.svg
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 1.3 MiB |
Submodule packages/core-api updated: fb2e615b50...af272fac24
Submodule packages/dev-engine updated: 206bdb5406...7296e0224e
Submodule packages/ember-core updated: db536b414c...612626582d
Submodule packages/ember-ui updated: fa277fd1b3...280301ec27
Submodule packages/fleetops updated: 8b52c1fc07...fac2832a54
Submodule packages/iam-engine updated: 4e25379a1b...d08771a1bd
1
packages/ledger
Submodule
1
packages/ledger
Submodule
Submodule packages/ledger added at 1f6f27f501
Submodule packages/pallet updated: 9d87fc26fe...424446db0e
1
packages/registry-bridge
Submodule
1
packages/registry-bridge
Submodule
Submodule packages/registry-bridge added at e75b37fc6c
Submodule packages/storefront updated: 845eba683c...720a81af29
Reference in New Issue
Block a user