Compare commits

..

3 Commits

Author SHA1 Message Date
Ronald A. Richardson
435552f332 updated docker-compose.yml to set baseUrl for solid server 2023-12-06 13:51:45 +08:00
Ronald A. Richardson
e0615c5a9b api setup to link to solid extension 2023-12-05 18:09:37 +08:00
Ronald A. Richardson
a830e190fa getting started with solid protocol integration, setting up oidc client 2023-12-05 18:06:23 +08:00
84 changed files with 4076 additions and 4935 deletions

View File

@@ -1,185 +0,0 @@
name: Fleetbase CI/CD
on:
push:
branches: [ "gcpdeploy/*" ]
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
env:
PROJECT: ${{ vars.PROJECT }}
REGISTRY: ${{ vars.REGISTRY }}
SOCKETCLUSTER_HOST: ${{ vars.SOCKETCLUSTER_HOST }}
API_HOST: ${{ vars.API_HOST }}
K8S_CLUSTER_NAME: ${{ vars.K8S_CLUSTER_NAME }}
K8S_CLUSTER_LOCATION: ${{ vars.K8S_CLUSTER_LOCATION }}
GCP_WORKLOAD_IDENTITY_PROVIDER: ${{ vars.GCP_WORKLOAD_IDENTITY_PROVIDER }}
GCP_SERVICE_ACCOUNT: ${{ vars.GCP_SERVICE_ACCOUNT }}
GCP: "True" # switches docker builds to GCP-style registry
jobs:
build_service:
name: Build and Deploy the Service
runs-on: ubuntu-latest
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
steps:
- name: Checkout Code
uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Set Dynamic ENV Vars
run: |
SHORT_COMMIT=$(echo $GITHUB_SHA | cut -c -8)
echo "VERSION=${SHORT_COMMIT}" >> $GITHUB_ENV
echo "STACK=$(basename $GITHUB_REF)" >> $GITHUB_ENV
echo "REGISTRY_HOST=$(dirname $(dirname $REGISTRY))" >> $GITHUB_ENV
- id: 'auth'
name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@v1'
with:
token_format: "access_token"
create_credentials_file: true
workload_identity_provider: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ env.GCP_SERVICE_ACCOUNT }}
- name: 'Set up Cloud SDK'
uses: 'google-github-actions/setup-gcloud@v1'
- id: 'get-credentials'
uses: 'google-github-actions/get-gke-credentials@v1'
with:
cluster_name: ${{ env.K8S_CLUSTER_NAME }}
location: ${{ env.K8S_CLUSTER_LOCATION }}
- uses: 'docker/login-action@v3'
with:
registry: ${{ env.REGISTRY_HOST }}
username: 'oauth2accesstoken'
password: '${{ steps.auth.outputs.access_token }}'
- name: Prepare Composer Auth Secret
run: |
if [[ -n "${{ secrets._GITHUB_AUTH_TOKEN }}" ]]; then
echo '{"github-oauth": {"github.com": "'${{ secrets._GITHUB_AUTH_TOKEN }}'"}}' > composer-auth.json
else
echo '{}' > composer-auth.json
fi
- name: nullify ssm-parent config
run: |
# this is needed to disable ssm-parent, which is used on AWS
echo > api/.ssm-parent.yaml
- name: Build and Release
uses: docker/bake-action@v2
env:
REGISTRY: ${{ env.REGISTRY }}
VERSION: ${{ env.VERSION }}
CACHE: type=gha
with:
push: true
files: |
./docker-bake.hcl
- name: deploy with helm
run: |
helm upgrade -i fleetbase infra/helm -n ${{ env.PROJECT }}-${{ env.STACK }} --set image.repository=${{ env.REGISTRY }} --set image.tag=${{ env.VERSION }} --set 'api_host=${{ env.API_HOST }}' --set 'socketcluster_host=${{ env.SOCKETCLUSTER_HOST }}' --set 'ingress.annotations.kubernetes\.io/ingress\.global-static-ip-name=${{ env.PROJECT }}-${{ env.STACK }}'
build_frontend:
name: Build and Deploy the Console
needs: [build_service]
runs-on: ubuntu-latest
permissions:
id-token: write # This is required for requesting the JWT
contents: read # This is required for actions/checkout
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true
- name: Set Dynamic ENV Vars
run: |
SHORT_COMMIT=$(echo $GITHUB_SHA | cut -c -8)
echo "VERSION=${SHORT_COMMIT}" >> $GITHUB_ENV
echo "STACK=$(basename $GITHUB_REF)" >> $GITHUB_ENV
- id: 'auth'
name: 'Authenticate to Google Cloud'
uses: 'google-github-actions/auth@v1'
with:
token_format: "access_token"
create_credentials_file: true
workload_identity_provider: ${{ env.GCP_WORKLOAD_IDENTITY_PROVIDER }}
service_account: ${{ env.GCP_SERVICE_ACCOUNT }}
- name: 'Set up Cloud SDK'
uses: 'google-github-actions/setup-gcloud@v1'
- name: Install Node.js
uses: actions/setup-node@v3
with:
node-version: 16
- uses: pnpm/action-setup@v2
name: Install pnpm
id: pnpm-install
with:
version: 8
run_install: false
- name: Get pnpm Store Directory
id: pnpm-cache
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
name: Setup pnpm Cache
with:
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Check for _GITHUB_AUTH_TOKEN and create .npmrc
run: |
if [[ -n "${{ secrets._GITHUB_AUTH_TOKEN }}" ]]; then
echo "//npm.pkg.github.com/:_authToken=${{ secrets._GITHUB_AUTH_TOKEN }}" > .npmrc
fi
working-directory: ./console
- name: Install dependencies
run: pnpm install
working-directory: ./console
- name: Build
env:
SOCKETCLUSTER_HOST: ${{ env.SOCKETCLUSTER_HOST }}
SOCKETCLUSTER_SECURE: "true"
SOCKETCLUSTER_PORT: "443"
API_HOST: ${{ env.API_HOST }}
run: |
set -eu
pnpm build
working-directory: ./console
- name: Deploy Console 🚀
run: |
set -eu
gcloud app deploy --appyaml console/app.yaml console/dist
# leave 2 versions
gcloud app versions list --filter="traffic_split=0" --sort-by '~version' --format 'value(version.id)' | sed '1d' | xargs -r gcloud app versions delete

21
.gitignore vendored
View File

@@ -17,19 +17,10 @@ api/composer-install-dev.sh
api/auth.json
act.sh
composer-auth.json
docker/database/*
docker/database/mysql/*
.talismanrc
verdaccio/minio
verdaccio/config/htpasswd
verdaccio/storage
# private packages
packages/billing
packages/flespi
packages/loconav
packages/internals
packages/billing-api
packages/billing-engine
packages/flespi-engine
packages/flespi-integration
packages/projectargus-engine
# wip
packages/solid
solid
verdaccio
docker/database/*
docker/database/mysql/*

3
.gitmodules vendored
View File

@@ -39,3 +39,6 @@
[submodule "docs/api-reference"]
path = docs/api-reference
url = git@github.com:fleetbase/api-reference.git
[submodule "packages/solid"]
path = packages/solid
url = git@github.com:fleetbase/solid.git

View File

@@ -9,9 +9,10 @@
"license": "MIT",
"require": {
"php": "^7.3|^8.0",
"fleetbase/core-api": "^1.3.7",
"fleetbase/fleetops-api": "^0.3.9",
"fleetbase/storefront-api": "^0.2.6",
"fleetbase/core-api": "^1.3.2",
"fleetbase/fleetops-api": "^0.3.5",
"fleetbase/storefront-api": "^0.2.4",
"fleetbase/solid-api": "^0.0.1",
"fruitcake/laravel-cors": "^2.0",
"guzzlehttp/guzzle": "^7.0.1",
"laravel/framework": "^8.75",
@@ -32,6 +33,12 @@
"nunomaduro/collision": "^5.10",
"phpunit/phpunit": "^9.5.10"
},
"repositories": [
{
"type": "path",
"url": "../packages/solid"
}
],
"autoload": {
"psr-4": {
"App\\": "app/",

1357
api/composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,7 +13,7 @@ return [
|
*/
'name' => env('APP_NAME', 'Fleetbase'),
'name' => env('APP_NAME', 'Laravel'),
/*
|--------------------------------------------------------------------------

View File

@@ -1,7 +1,5 @@
<?php
use Fleetbase\Support\Utils;
return [
/*
@@ -21,7 +19,7 @@ return [
'allowed_methods' => ['*'],
'allowed_origins' => array_filter(['http://localhost:4200', env('CONSOLE_HOST'), Utils::addWwwToUrl(env('CONSOLE_URL'))]),
'allowed_origins' => ['http://localhost:4200', env('CONSOLE_HOST')],
'allowed_origins_patterns' => [],

View File

@@ -32,13 +32,13 @@ return [
'local' => [
'driver' => 'local',
'root' => storage_path('app')
'root' => storage_path('app'),
],
'public' => [
'driver' => 'local',
'root' => storage_path('app/public'),
'url' => env('APP_URL') . '/storage',
'url' => env('APP_URL').'/storage',
],
's3' => [
@@ -51,14 +51,6 @@ return [
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
],
'gcs' => [
'driver' => 'gcs',
'project_id' => env('GOOGLE_CLOUD_PROJECT_ID', 'your-project-id'),
'key_file' => env('GOOGLE_CLOUD_KEY_FILE', null),
'bucket' => env('GOOGLE_CLOUD_STORAGE_BUCKET', env('AWS_BUCKET')),
'path_prefix' => env('GOOGLE_CLOUD_STORAGE_PATH_PREFIX', null),
'storage_api_uri' => env('GOOGLE_CLOUD_STORAGE_API_URI', env('AWS_URL')),
],
],
/*
@@ -73,7 +65,8 @@ return [
*/
'links' => [
public_path('storage') => storage_path('app/public')
public_path('storage') => storage_path('app/public'),
public_path('uploads') => storage_path('app/uploads'),
],
];

View File

@@ -60,10 +60,6 @@ return [
'transport' => 'postmark',
],
'sendgrid' => [
'transport' => 'sendgrid',
],
'sendmail' => [
'transport' => 'sendmail',
'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -t -i'),
@@ -100,7 +96,7 @@ return [
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@fleetbase.io'),
'name' => env('MAIL_FROM_NAME', env('APP_NAME', 'Fleetbase')),
'name' => env('MAIL_FROM_NAME', 'Fleetbase'),
],
/*

View File

@@ -30,8 +30,4 @@ return [
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
'sendgrid' => [
'api_key' => env('SENDGRID_API_KEY'),
],
];

View File

@@ -13,7 +13,4 @@ php artisan migrate --force
php artisan sandbox:migrate --force
# Seed database
php artisan fleetbase:seed
# Restart queue
php artisan queue:restart
php artisan fleetbase:seed

1
api/public/uploads Symbolic link
View File

@@ -0,0 +1 @@
/var/www/html/api/storage/app/uploads

View File

@@ -1,7 +1,15 @@
{
/**
Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript
rather than JavaScript by default, when a TypeScript version of a given blueprint is available.
Ember CLI sends analytics information by default. The data is completely
anonymous, but there are times when you might want to disable this behavior.
Setting `disableAnalytics` to true will prevent any data from being sent.
*/
"disableAnalytics": false,
/**
Setting `isTypeScriptProject` to true will force the blueprint generators to generate TypeScript
rather than JavaScript by default, when a TypeScript version of a given blueprint is available.
*/
"isTypeScriptProject": false
}

View File

@@ -1,13 +1,25 @@
# unconventional js
/blueprints/*/files/
/vendor/
# compiled output
/dist/
/tmp/
# dependencies
/bower_components/
/node_modules/
# misc
/coverage/
!.*
.*/
.eslintcache
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/npm-shrinkwrap.json.ember-try
/package.json.ember-try
/package-lock.json.ember-try
/yarn.lock.ember-try

View File

@@ -2,13 +2,12 @@
module.exports = {
root: true,
parser: '@babel/eslint-parser',
parser: 'babel-eslint',
parserOptions: {
ecmaVersion: 'latest',
ecmaVersion: 2018,
sourceType: 'module',
requireConfigFile: false,
babelOptions: {
plugins: [['@babel/plugin-proposal-decorators', { decoratorsBeforeExport: true }]],
ecmaFeatures: {
legacyDecorators: true,
},
},
plugins: ['ember'],
@@ -30,7 +29,7 @@ module.exports = {
'ember/no-empty-glimmer-component-classes': 'off',
'ember/no-get': 'off',
'ember/classic-decorator-no-classic-methods': 'off',
'n/no-unpublished-require': [
'node/no-unpublished-require': [
'error',
{
allowModules: [
@@ -51,18 +50,18 @@ module.exports = {
'no-prototype-builtins': 'off',
},
overrides: [
// node files
{
files: [
'./.eslintrc.js',
'./.prettierrc.js',
'./.stylelintrc.js',
'./.template-lintrc.js',
'./ember-cli-build.js',
'./index.js',
'./testem.js',
'./blueprints/*/index.js',
'./config/**/*.js',
'./lib/*/index.js',
'./server/**/*.js',
'./tests/dummy/config/**/*.js',
],
parserOptions: {
sourceType: 'script',
@@ -71,7 +70,13 @@ module.exports = {
browser: false,
node: true,
},
extends: ['plugin:n/recommended'],
plugins: ['node'],
extends: ['plugin:node/recommended'],
},
{
// test files
files: ['tests/**/*-test.{js,ts}'],
extends: ['plugin:qunit/recommended'],
},
],
};

View File

@@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
node-version: [18.x] # Build on Node.js 18
node-version: [16.x] # Build on Node.js 16
steps:
- uses: actions/checkout@v2
@@ -57,4 +57,4 @@ jobs:
run: pnpm run lint
- name: Build
run: npx ember build --environment production
run: npx ember build --environment production

9
console/.gitignore vendored
View File

@@ -1,16 +1,22 @@
# See https://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist/
/declarations/
/tmp/
# dependencies
/bower_components/
/node_modules/
# misc
/.env*
/environments/.env*
/.pnp*
/.sass-cache
/.eslintcache
/connect.lock
/coverage/
/libpeerconnection.log
/npm-debug.log*
/testem.log
/yarn-error.log
@@ -18,6 +24,7 @@
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/npm-shrinkwrap.json.ember-try
/package.json.ember-try
/package-lock.json.ember-try

View File

@@ -1,13 +1,25 @@
# unconventional js
/blueprints/*/files/
/vendor/
# compiled output
/dist/
/tmp/
# dependencies
/bower_components/
/node_modules/
# misc
/coverage/
!.*
.*/
.eslintcache
.lint-todo/
# ember-try
/.node_modules.ember-try/
/bower.json.ember-try
/npm-shrinkwrap.json.ember-try
/package.json.ember-try
/package-lock.json.ember-try
/yarn.lock.ember-try

View File

@@ -8,7 +8,7 @@ module.exports = {
printWidth: 190,
overrides: [
{
files: '*.{hbs,js,ts}',
files: '*.hbs',
options: {
singleQuote: false,
},

View File

@@ -1,8 +0,0 @@
# unconventional files
/blueprints/*/files/
# compiled output
/dist/
# addons
/.node_modules.ember-try/

View File

@@ -1,8 +0,0 @@
'use strict';
module.exports = {
extends: ['stylelint-config-standard', 'stylelint-prettier/recommended'],
rules: {
'import-notation': null,
},
};

View File

@@ -6,6 +6,7 @@ module.exports = {
'no-bare-strings': 'off',
'no-invalid-interactive': 'off',
'no-yield-only': 'off',
'no-down-event-binding': 'off',
'table-groups': 'off',
'link-href-attributes': 'off',
'require-input-label': 'off',

View File

@@ -1,3 +1,3 @@
{
"ignore_dirs": ["dist"]
"ignore_dirs": ["tmp", "dist"]
}

View File

@@ -1,12 +0,0 @@
runtime: python312
handlers:
- url: /(.*\..+)$
static_files: \1
upload: (.+)
secure: always
expiration: 1h
- url: /.*
static_files: index.html
upload: index.html
secure: always

View File

@@ -5,14 +5,14 @@
<Spinner />
</div>
{{else}}
<div class="flex flex-row p-3 border-b dark:border-gray-700 border-gray-200">
<div class="flex flex-row p-4 border-b dark:border-gray-700 border-gray-200">
<div class="w-12 flex-shrink-0"><img src={{this.data.owner.avatar_url}} alt="fleetbase/fleetbase" class="rounded-full w-8 h-8" width="32" height="32" /></div>
<div class="flex-1 -mt-2">
<div class="flex flex-1 flex-row items-center justify-between mb-2">
<a href={{this.data.html_url}} target="_github" class="dark:text-gray-100 text-black text-base font-semibold">{{this.data.full_name}}</a>
<a href={{this.data.html_url}} target="_github" class="dark:text-gray-100 text-black text-lg font-semibold">{{this.data.full_name}}</a>
<a href={{this.data.html_url}} target="_github" class="btn btn-xs btn-default">
<FaIcon @icon="star" class="text-yellow-400" />
<span class="hidden truncate lg:flex ml-2.5">Star on Github</span>
<span class="hidden lg:flex ml-2.5">Star on Github</span>
</a>
</div>
<p class="dark:text-gray-100 text-black text-sm">{{this.data.description}}</p>
@@ -40,7 +40,7 @@
</div>
<div class="col-span-3 dark:text-gray-100 text-black text-xs flex flex-row">
<span class="font-bold mr-1">{{this.data.open_issues_count}}</span>
<span class="truncate"><span class="hidden lg:inline-flex">Open</span> Issues</span>
<span><span class="hidden lg:inline-flex">Open</span> Issues</span>
</div>
</div>
</div>

View File

@@ -33,20 +33,6 @@ export default class AuthLoginController extends Controller {
*/
@service session;
/**
* Inject the `router` service
*
* @var {Service}
*/
@service router;
/**
* Inject the `fetch` service
*
* @var {Service}
*/
@service fetch;
/**
* Whether or not to remember the users session
*
@@ -115,16 +101,6 @@ export default class AuthLoginController extends Controller {
// get user credentials
const { email, password, rememberMe } = this;
// If no password error
if (!email) {
return this.notifications.warning('Did you forget to enter your email?');
}
// If no password error
if (!password) {
return this.notifications.warning('Did you forget to enter your password?');
}
// start loader
this.set('isLoading', true);
@@ -136,11 +112,6 @@ export default class AuthLoginController extends Controller {
} catch (error) {
this.failedAttempts++;
// Handle unverified user
if (error.toString().includes('not verified')) {
return this.sendUserForEmailVerification(email);
}
return this.failure(error);
}
@@ -153,44 +124,20 @@ export default class AuthLoginController extends Controller {
* Transition user to onboarding screen
*/
@action transitionToOnboard() {
return this.router.transitionTo('onboard');
return this.transitionToRoute('onboard');
}
/**
* Transition to forgot password screen, if email is set - set it.
*/
@action forgotPassword() {
return this.router.transitionTo('auth.forgot-password').then(() => {
return this.transitionToRoute('auth.forgot-password').then(() => {
if (this.email) {
this.forgotPasswordController.email = this.email;
}
});
}
/**
* Creates an email verification session and transitions user to verification route.
*
* @param {String} email
* @return {Promise<Transition>}
* @memberof AuthLoginController
*/
sendUserForEmailVerification(email) {
return this.fetch.post('auth/create-verification-session', { email, send: true }).then(({ token }) => {
return this.session.store.persist({ email }).then(() => {
this.notifications.warning('Your account needs to be verified to proceed.');
return this.router
.transitionTo('auth.verification', {
queryParams: {
token,
},
})
.then(() => {
this.reset('error');
});
});
});
}
/**
* Sets correct route to send user to after login.
*

View File

@@ -18,13 +18,6 @@ export default class AuthResetPasswordController extends Controller {
*/
@service notifications;
/**
* Inject the `router` service
*
* @memberof AuthResetPasswordController
*/
@service router;
/**
* The code param.
*
@@ -72,7 +65,7 @@ export default class AuthResetPasswordController extends Controller {
.then(() => {
this.notifications.success('Your password has been reset! Login to continue.');
return this.router.transitionTo('auth.login');
return this.transitionToRoute('auth.login');
})
.catch((error) => {
this.notifications.serverError(error);

View File

@@ -1,220 +0,0 @@
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { later } from '@ember/runloop';
import { not } from '@ember/object/computed';
export default class AuthVerificationController extends Controller {
/**
* Inject the `fetch` service
*
* @memberof OnboardIndexController
*/
@service fetch;
/**
* Inject the `notifications` service
*
* @memberof OnboardIndexController
*/
@service notifications;
/**
* Inject the `modalsManager` service
*
* @memberof OnboardIndexController
*/
@service modalsManager;
/**
* Inject the `currentUser` service
*
* @memberof OnboardIndexController
*/
@service currentUser;
/**
* Inject the `router` service
*
* @memberof OnboardIndexController
*/
@service router;
/**
* Inject the `session` service
*
* @memberof OnboardIndexController
*/
@service session;
/**
* The session paramerer.
*
* @memberof OnboardVerifyEmailController
*/
@tracked hello;
/**
* The token paramerer.
*
* @memberof OnboardVerifyEmailController
*/
@tracked token;
/**
* The loading state of the verification request.
*
* @memberof OnboardVerifyEmailController
*/
@tracked isLoading = false;
/**
* Validation state tracker.
*
* @memberof OnboardVerifyEmailController
*/
@tracked isReadyToSubmit = false;
/**
* The request timeout to trigger alternative verification options.
*
* @memberof OnboardVerifyEmailController
*/
@tracked waitTimeout = 800 * 60 * 2;
/**
* Determines if Fleetbase is still awaiting verification after a certain time.
*
* @memberof OnboardVerifyEmailController
*/
@tracked stillWaiting = false;
/**
* the input code.
*
* @memberof OnboardVerifyEmailController
*/
@tracked code;
/**
* The query param for the session token.
*
* @memberof OnboardVerifyEmailController
*/
@tracked queryParams = ['hello', 'token'];
/**
* The boolean opposite of `isReadyToSubmit`
*
* @memberof OnboardVerifyEmailController
*/
@not('isReadyToSubmit') isNotReadyToSubmit;
/**
* Creates an instance of OnboardVerifyEmailController.
* @memberof OnboardVerifyEmailController
*/
constructor() {
super(...arguments);
later(
this,
() => {
this.stillWaiting = true;
},
this.waitTimeout
);
}
/**
* Validates the input
*
* @param {InputEvent} { target: { value } }
* @memberof OnboardVerifyEmailController
*/
@action validateInput({ target: { value } }) {
if (value.length > 5) {
this.isReadyToSubmit = true;
} else {
this.isReadyToSubmit = false;
}
}
/**
* Submits to verify code.
*
* @return {Promise}
* @memberof OnboardVerifyEmailController
*/
@action verifyCode() {
const { token, code, email } = this;
this.isLoading = true;
return this.fetch
.post('auth/verify-email', { token, code, email, authenticate: true })
.then(({ status, token }) => {
if (status === 'ok') {
this.notifications.success('Email successfully verified!');
if (token) {
this.notifications.info('Welcome to Fleetbase!');
this.session.manuallyAuthenticate(token);
return this.router.transitionTo('console');
}
return this.router.transitionTo('auth.login');
}
})
.catch((error) => {
this.notifications.serverError(error);
})
.finally(() => {
this.isLoading = false;
});
}
/**
* Action to resend verification code by SMS.
*
* @memberof OnboardVerifyEmailController
*/
@action resendBySms() {
this.modalsManager.show('modals/verify-by-sms', {
title: 'Verify Account by Phone',
acceptButtonText: 'Send',
phone: this.currentUser.phone,
confirm: (modal) => {
modal.startLoading();
const phone = modal.getOption('phone');
return this.fetch.post('onboard/send-verification-sms', { phone }).then(() => {
this.notifications.success('Verification code SMS sent!');
});
},
});
}
/**
* Action to resend verification code by email.
*
* @memberof OnboardVerifyEmailController
*/
@action resendEmail() {
this.modalsManager.show('modals/resend-verification-email', {
title: 'Resend Verification Code',
acceptButtonText: 'Send',
email: this.currentUser.email,
confirm: (modal) => {
modal.startLoading();
const email = modal.getOption('email');
return this.fetch.post('onboard/send-verification-email', { email }).then(() => {
this.notifications.success('Verification code email sent!');
});
},
});
}
}

View File

@@ -72,20 +72,6 @@ export default class ConsoleController extends Controller {
*/
@tracked sidebarContext;
/**
* State of sidebar toggle icon
*
* @var {SidebarContext}
*/
@tracked sidebarToggleEnabled = true;
/**
* The sidebar toggle state.
*
* @var {SidebarContext}
*/
@tracked sidebarToggleState = {};
/**
* Routes which should hide the sidebar menu.
*
@@ -118,47 +104,15 @@ export default class ConsoleController extends Controller {
this.router.on('routeDidChange', (transition) => {
if (this.sidebarContext) {
// Determine if the new route should hide the sidebar
const shouldHideSidebar = this.hiddenSidebarRoutes.includes(transition.to.name);
// Check if the sidebar was manually toggled and is currently closed
const isSidebarManuallyClosed = this.sidebarToggleState.clicked && !this.sidebarToggleState.isOpen;
// Hide the sidebar if the current route is in hiddenSidebarRoutes
if (shouldHideSidebar) {
this.sidebarContext.hideNow();
this.sidebarToggleEnabled = false;
return; // Exit early as no further action is required
}
// If the sidebar was manually closed and not on a hidden route, keep it closed
if (isSidebarManuallyClosed) {
if (this.hiddenSidebarRoutes.includes(transition.to.name)) {
this.sidebarContext.hideNow();
} else {
// Otherwise, show the sidebar
this.sidebarContext.show();
}
// Ensure toggle is enabled unless on a hidden route
this.sidebarToggleEnabled = !shouldHideSidebar;
}
});
}
/**
* When sidebar is manually toggled
*
* @param {SidebarContext} sidebar
* @param {boolean} isOpen
* @memberof ConsoleController
*/
@action onSidebarToggle(sidebar, isOpen) {
this.sidebarToggleState = {
clicked: true,
isOpen,
};
}
/**
* Sets the sidebar context
*
@@ -171,7 +125,6 @@ export default class ConsoleController extends Controller {
if (this.hiddenSidebarRoutes.includes(this.router.currentRouteName)) {
this.sidebarContext.hideNow();
this.sidebarToggleEnabled = false;
}
}

View File

@@ -104,16 +104,6 @@ export default class ConsoleAdminBrandingController extends Controller {
.save()
.then(() => {
this.notifications.success('Branding settings saved.');
// if logo url is null
if (this.model.logo_url === null) {
this.model.set('logo_url', '/images/fleetbase-logo-svg.svg');
}
// if icon url is null
if (this.model.icon_url === null) {
this.model.set('icon_url', '/images/icon.png');
}
})
.finally(() => {
this.isLoading = false;

View File

@@ -7,7 +7,6 @@ import { task } from 'ember-concurrency-decorators';
export default class InstallController extends Controller {
@service fetch;
@service notifications;
@service router;
@tracked error;
@tracked steps = [
{ task: 'createdb', name: 'Create Database', status: 'pending' },
@@ -47,7 +46,7 @@ export default class InstallController extends Controller {
if (isCompleted) {
this.notifications.success('Install completed successfully!');
return this.router.transitionTo('onboard');
return this.transitionToRoute('onboard');
}
}

View File

@@ -8,7 +8,6 @@ export default class InviteForUserController extends Controller {
@service session;
@service notifications;
@service modalsManager;
@service router;
@tracked code;
@tracked isLoading;
@@ -25,7 +24,7 @@ export default class InviteForUserController extends Controller {
this.isLoading = false;
return this.router.transitionTo('console').then(() => {
return this.transitionToRoute('console').then(() => {
if (response.needs_password && response.needs_password === true) {
this.setPassword();
}

View File

@@ -21,13 +21,6 @@ export default class OnboardIndexController extends Controller {
*/
@service session;
/**
* Inject the `router` service
*
* @memberof OnboardIndexController
*/
@service router;
/**
* Inject the `notifications` service
*
@@ -124,18 +117,17 @@ export default class OnboardIndexController extends Controller {
return this.fetch
.post('onboard/create-account', input)
.then(({ status, skipVerification, token, session }) => {
if (status === 'success') {
if (skipVerification === true && token) {
// only manually authenticate if skip verification
this.session.isOnboarding().manuallyAuthenticate(token);
.then((response) => {
if (response.status === 'success') {
this.session.isOnboarding().manuallyAuthenticate(response.token);
return this.router.transitionTo('console').then(() => {
if (response.skipVerification === true) {
return this.transitionToRoute('console').then(() => {
this.notifications.success('Welcome to Fleetbase!');
});
}
return this.router.transitionTo('onboard.verify-email', { queryParams: { hello: session } });
return this.transitionToRoute('onboard.verify-email', { queryParams: { hello: response.session } });
}
})
.catch((error) => {

View File

@@ -1,7 +1,125 @@
import AuthVerificationController from '../auth/verification';
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { later } from '@ember/runloop';
import { not } from '@ember/object/computed';
export default class OnboardVerifyEmailController extends Controller {
/**
* Inject the `fetch` service
*
* @memberof OnboardIndexController
*/
@service fetch;
/**
* Inject the `notifications` service
*
* @memberof OnboardIndexController
*/
@service notifications;
/**
* Inject the `modalsManager` service
*
* @memberof OnboardIndexController
*/
@service modalsManager;
/**
* Inject the `currentUser` service
*
* @memberof OnboardIndexController
*/
@service currentUser;
/**
* The session paramerer.
*
* @memberof OnboardVerifyEmailController
*/
@tracked hello;
/**
* The loading state of the verification request.
*
* @memberof OnboardVerifyEmailController
*/
@tracked isLoading = false;
/**
* Validation state tracker.
*
* @memberof OnboardVerifyEmailController
*/
@tracked isReadyToSubmit = false;
/**
* The request timeout to trigger alternative verification options.
*
* @memberof OnboardVerifyEmailController
*/
@tracked waitTimeout = 800 * 60 * 2;
/**
* Determines if Fleetbase is still awaiting verification after a certain time.
*
* @memberof OnboardVerifyEmailController
*/
@tracked stillWaiting = false;
/**
* the input code.
*
* @memberof OnboardVerifyEmailController
*/
@tracked code;
/**
* The query param for the session token.
*
* @memberof OnboardVerifyEmailController
*/
@tracked queryParams = ['hello'];
/**
* The boolean opposite of `isReadyToSubmit`
*
* @memberof OnboardVerifyEmailController
*/
@not('isReadyToSubmit') isNotReadyToSubmit;
/**
* Creates an instance of OnboardVerifyEmailController.
* @memberof OnboardVerifyEmailController
*/
constructor() {
super(...arguments);
later(
this,
() => {
this.stillWaiting = true;
},
this.waitTimeout
);
}
/**
* Validates the input
*
* @param {InputEvent} { target: { value } }
* @memberof OnboardVerifyEmailController
*/
@action validateInput({ target: { value } }) {
if (value.length > 5) {
this.isReadyToSubmit = true;
} else {
this.isReadyToSubmit = false;
}
}
export default class OnboardVerifyEmailController extends AuthVerificationController {
/**
* Submits to verify code.
*
@@ -9,24 +127,19 @@ export default class OnboardVerifyEmailController extends AuthVerificationContro
* @memberof OnboardVerifyEmailController
*/
@action verifyCode() {
const { hello, code } = this;
const session = this.hello;
const code = this.code;
this.isLoading = true;
return this.fetch
.post('onboard/verify-email', { session: hello, code })
.then(({ status, token }) => {
if (status === 'ok') {
.post('onboard/verify-email', { session, code })
.then((response) => {
if (response.status === 'success') {
this.notifications.success('Email successfully verified!');
this.notifications.info('Welcome to Fleetbase!');
if (token) {
this.notifications.info('Welcome to Fleetbase!');
this.session.manuallyAuthenticate(token);
return this.router.transitionTo('console');
}
return this.router.transitionTo('auth.login');
return this.transitionToRoute('console');
}
})
.catch((error) => {
@@ -36,4 +149,46 @@ export default class OnboardVerifyEmailController extends AuthVerificationContro
this.isLoading = false;
});
}
/**
* Action to resend verification code by SMS.
*
* @memberof OnboardVerifyEmailController
*/
@action resendBySms() {
this.modalsManager.show('modals/verify-by-sms', {
title: 'Verify Account by Phone',
acceptButtonText: 'Send',
phone: this.currentUser.phone,
confirm: (modal) => {
modal.startLoading();
const phone = modal.getOption('phone');
return this.fetch.post('onboard/send-verification-sms', { phone }).then(() => {
this.notifications.success('Verification code SMS sent!');
});
},
});
}
/**
* Action to resend verification code by email.
*
* @memberof OnboardVerifyEmailController
*/
@action resendEmail() {
this.modalsManager.show('modals/resend-verification-email', {
title: 'Resend Verification Code',
acceptButtonText: 'Send',
email: this.currentUser.email,
confirm: (modal) => {
modal.startLoading();
const email = modal.getOption('email');
return this.fetch.post('onboard/send-verification-email', { email }).then(() => {
this.notifications.success('Verification code email sent!');
});
},
});
}
}

View File

@@ -11,7 +11,6 @@ Router.map(function () {
this.route('login', { path: '/' });
this.route('forgot-password');
this.route('reset-password');
this.route('verification');
});
this.route('onboard', function () {
this.route('verify-email');

View File

@@ -11,7 +11,6 @@ export default class ApplicationRoute extends Route {
@service urlSearchParams;
@service modalsManager;
@service intl;
@service router;
@tracked defaultTheme;
/**
@@ -28,11 +27,11 @@ export default class ApplicationRoute extends Route {
this.defaultTheme = defaultTheme;
if (shouldInstall) {
return this.router.transitionTo('install');
return this.transitionTo('install');
}
if (shouldOnboard) {
return this.router.transitionTo('onboard');
return this.transitionTo('onboard');
}
}
@@ -50,7 +49,7 @@ export default class ApplicationRoute extends Route {
const shift = this.urlSearchParams.get('shift');
if (isAuthenticated && shift) {
return this.router.transitionTo(pathToRoute(shift));
return this.transitionTo(pathToRoute(shift));
}
}

View File

@@ -1,33 +0,0 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default class AuthVerificationRoute extends Route {
@service session;
@service fetch;
@service router;
queryParams = {
token: {
refreshModel: false,
replace: true,
},
};
beforeModel(transition) {
let { token } = transition.to.queryParams;
return this.session.store.restore().then(({ email }) => {
return this.fetch.post('auth/validate-verification-session', { email, token }).then(({ valid }) => {
if (!valid) {
return this.router.transitionTo('auth.login');
}
});
});
}
async setupController(controller) {
super.setupController(...arguments);
let { email } = await this.session.store.restore();
controller.email = email;
}
}

View File

@@ -4,12 +4,11 @@ import { inject as service } from '@ember/service';
export default class ConsoleAdminRoute extends Route {
@service currentUser;
@service notifications;
@service router;
beforeModel() {
// USER MUST BE ADMIN
if (!this.currentUser.user.is_admin) {
return this.router.transitionTo('console').then(() => {
return this.transitionTo('console').then(() => {
this.notifications.error('You do not have authorization to access admin!');
});
}

View File

@@ -1,4 +1,4 @@
@import 'tailwindcss/base';
@import 'tailwindcss/components';
@import 'tailwindcss/utilities';
@import 'inter-ui/inter.css';
@import 'inter-ui/inter.css';

View File

@@ -1,46 +0,0 @@
{{page-title "Account Verification"}}
<div class="bg-white dark:bg-gray-800 py-8 px-4 shadow rounded-lg w-full">
<div class="mb-8">
<img class="mx-auto h-12 w-auto " src="/images/fleetbase-logo-svg.svg" alt={{t "app.name"}}>
<h2 class="mt-6 text-center text-lg font-extrabold text-gray-900 dark:text-white truncate">
Verify your email address
</h2>
</div>
<div class="flex px-3 py-2 mb-6 rounded-md shadow-sm bg-blue-200">
<div>
<FaIcon @icon="shield-check" @size="lg" class="text-blue-900 mr-4" />
</div>
<p class="flex-1 text-sm text-blue-900 dark:text-blue-900">
<strong>Almost done!</strong><br> Check your email for a verification code.
</p>
</div>
<form class="mt-8 space-y-6" {{on "submit" this.verifyCode}}>
<InputGroup @type="tel" @name="Verification Code" @value={{this.code}} @helpText="Enter the verification code you received via email." @inputClass="input-lg" {{on "input" this.validateInput}} />
<div>
<Button @icon="check" @iconPrefix="fas" @buttonType="submit" @type="primary" @size="lg" @text="Verify & Continue" @isLoading={{this.isLoading}} @disabled={{this.isNotReadyToSubmit}} @onClick={{this.verifyCode}} />
</div>
{{#if this.stillWaiting}}
<div class="bg-yellow-50 rounded shadow-sm border-l-4 border-yellow-400 px-4 py-2">
<div class="flex">
<div class="flex-shrink-0">
<FaIcon @icon="exclamation-triangle" @size="lg" class="text-yellow-400" />
</div>
<div class="ml-3 flex items-center">
<span class="text-lg font-extrabold text-yellow-800">Didn't receive an email yet?</span>
</div>
</div>
<div class="py-3">
<p class="text-yellow-700">Use alternaitve options below to verify your account.</p>
<div class="flex items-center mt-3">
<Button @type="default" class="mr-2" @onClick={{this.resendEmail}}>Resend Email</Button>
<Button @type="default" @onClick={{this.resendBySms}}>Send by SMS</Button>
</div>
</div>
</div>
{{/if}}
</form>
</div>

View File

@@ -1,6 +1,6 @@
{{page-title "Console"}}
<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}} @user={{this.user}} @organizations={{this.organizations}} @menuItems={{this.universe.headerMenuItems}} @extensions={{this.extensions}} @onAction={{this.onAction}} />
<Layout::Main>
<Layout::Sidebar @onSetup={{this.setSidebarContext}}>
<div class="next-sidebar-content-inner">

View File

@@ -1,6 +1,6 @@
{{page-title "Dashboard"}}
<Layout::Section::Body class="overflow-y-scroll h-full pt-6">
<div class="console-home-container mx-auto h-screen space-y-4" {{increase-height-by 300}}>
<div class="container mx-auto h-screen space-y-4" {{increase-height-by 300}}>
<div class="grid grid-cols-1 md:grid-cols-12 gap-4">
<GithubCard class="lg:col-span-4" />
<FleetbaseBlog class="lg:col-span-8" />

View File

@@ -1,4 +1,3 @@
{{page-title "Account Verification"}}
<div class="bg-white dark:bg-gray-800 py-8 px-4 shadow rounded-lg w-full">
<div class="mb-8">
<img class="mx-auto h-12 w-auto " src="/images/fleetbase-logo-svg.svg" alt={{t "app.name"}}>

View File

@@ -3,7 +3,7 @@
"packages": [
{
"name": "ember-cli",
"version": "5.4.1",
"version": "4.6.0",
"blueprints": [
{
"name": "app",

View File

@@ -4,13 +4,12 @@ const getenv = require('./utils/getenv');
const fixApiHost = require('./utils/fix-api-host');
module.exports = function (environment) {
const ENV = {
let ENV = {
modulePrefix: '@fleetbase/console',
environment,
rootURL: '/',
locationType: 'history',
EmberENV: {
EXTEND_PROTOTYPES: true,
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true

View File

@@ -13,7 +13,7 @@ const autoprefixer = require('autoprefixer');
const tailwind = require('tailwindcss');
module.exports = function (defaults) {
const app = new EmberApp(defaults, {
let app = new EmberApp(defaults, {
storeConfigInMeta: false,
fingerprint: {

View File

@@ -1,11 +1,11 @@
{
"name": "@fleetbase/console",
"version": "0.3.6",
"version": "0.3.1",
"private": true,
"description": "Fleetbase Console",
"repository": "https://github.com/fleetbase/fleetbase",
"repository": "",
"license": "MIT",
"author": "Fleetbase Pte Ltd <hello@fleetbase.io>",
"author": "",
"directories": {
"doc": "doc",
"test": "tests"
@@ -13,35 +13,33 @@
"scripts": {
"prebuild": "node prebuild.js",
"build": "pnpm run prebuild && ember build --environment=production",
"lint": "concurrently \"npm:lint:*(!fix)\" --names \"lint:\"",
"lint:css": "stylelint \"**/*.css\"",
"lint:css:fix": "concurrently \"npm:lint:css -- --fix\"",
"lint:fix": "concurrently \"npm:lint:*:fix\" --names \"fix:\"",
"lint": "npm-run-all --aggregate-output --continue-on-error --parallel \"lint:!(fix)\"",
"lint:fix": "npm-run-all --aggregate-output --continue-on-error --parallel lint:*:fix",
"lint:hbs": "ember-template-lint .",
"lint:hbs:fix": "ember-template-lint . --fix",
"lint:js": "eslint . --cache",
"lint:js:fix": "eslint . --fix",
"start": "pnpm run prebuild && ember serve",
"test": "concurrently \"npm:lint\" \"npm:test:*\" --names \"lint,test:\"",
"test": "npm-run-all lint test:*",
"test:ember": "ember test"
},
"dependencies": {
"@fleetbase/ember-core": "^0.2.0",
"@fleetbase/ember-ui": "^0.2.9",
"@fleetbase/storefront-engine": "^0.2.6",
"@fleetbase/fleetops-engine": "^0.3.9",
"@fleetbase/fleetops-data": "^0.1.7",
"@fleetbase/dev-engine": "^0.2.0",
"@fleetbase/iam-engine": "^0.0.8",
"@fleetbase/leaflet-routing-machine": "^3.2.16",
"@ember/legacy-built-in-components": "^0.4.1",
"@fleetbase/ember-core": "^0.1.8",
"@fleetbase/ember-ui": "^0.2.6",
"@fleetbase/fleetops-data": "^0.1.5",
"@fleetbase/fleetops-engine": "^0.3.5",
"@fleetbase/storefront-engine": "^0.2.4",
"@fleetbase/dev-engine": "^0.1.9",
"@fleetbase/iam-engine": "^0.0.7",
"@fleetbase/leaflet-routing-machine": "^3.2.16",
"@fortawesome/ember-fontawesome": "^0.4.1",
"ember-changeset": "^4.1.2",
"ember-changeset-validations": "^4.1.1",
"ember-composable-helpers": "^5.0.0",
"ember-concurrency": "^3.0.0",
"ember-concurrency-decorators": "^2.0.3",
"ember-intl": "6.3.2",
"ember-intl": "6.0.0-beta.6",
"ember-math-helpers": "^2.18.2",
"ember-power-select": "^6.0.1",
"ember-prism": "^0.13.0",
@@ -53,12 +51,8 @@
"postcss-nth-list": "^1.0.2"
},
"devDependencies": {
"@babel/core": "^7.23.2",
"@babel/eslint-parser": "^7.22.15",
"@babel/plugin-proposal-decorators": "^7.23.2",
"@ember/optional-features": "^2.0.0",
"@ember/string": "^3.1.1",
"@ember/test-helpers": "^3.2.0",
"@ember/test-helpers": "^2.8.1",
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-brands-svg-icons": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
@@ -66,49 +60,49 @@
"@glimmer/tracking": "^1.1.2",
"@tailwindcss/forms": "^0.5.3",
"autoprefixer": "^10.4.8",
"babel-eslint": "^10.1.0",
"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.4.2",
"ember-cli": "~4.6.0",
"ember-cli-app-version": "^6.0.1",
"ember-cli-babel": "^8.2.0",
"ember-cli-clean-css": "^3.0.0",
"ember-cli-dependency-checker": "^3.3.2",
"ember-cli-babel": "^7.26.11",
"ember-cli-dependency-checker": "^3.3.1",
"ember-cli-dotenv": "^3.1.0",
"ember-cli-htmlbars": "^6.3.0",
"ember-cli-es6-transform": "^1.0.0",
"ember-cli-htmlbars": "^6.1.0",
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-postcss": "^8.2.0",
"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-data": "^4.6.1",
"ember-engines": "^0.8.23",
"ember-fetch": "^8.1.2",
"ember-fetch": "^8.1.1",
"ember-leaflet": "^5.1.1",
"ember-load-initializers": "^2.1.2",
"ember-modifier": "^4.1.0",
"ember-page-title": "^8.0.0",
"ember-qunit": "^8.0.1",
"ember-resolver": "^11.0.1",
"ember-page-title": "^7.0.0",
"ember-qunit": "^5.1.5",
"ember-resolver": "^8.0.3",
"ember-responsive": "^5.0.0",
"ember-source": "~5.4.0",
"ember-template-lint": "^5.11.2",
"ember-source": "~4.6.0",
"ember-template-lint": "^4.10.1",
"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",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-ember": "^11.0.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-qunit": "^7.3.1",
"fast-glob": "^3.3.0",
"fs": "0.0.1-security",
"inter-ui": "^3.19.3",
"leaflet": "^1.9.4",
"loader.js": "^4.7.0",
"normalize.css": "^8.0.1",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.21",
"postcss-conditionals-renewed": "^1.0.0",
"postcss-each": "^1.1.0",
@@ -116,28 +110,24 @@
"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",
"prettier": "^2.7.1",
"qunit": "^2.19.1",
"qunit-dom": "^2.0.0",
"recast": "^0.23.3",
"stylelint": "^15.11.0",
"stylelint-config-standard": "^34.0.0",
"stylelint-prettier": "^4.0.2",
"tailwindcss": "^3.1.8",
"tracked-built-ins": "^3.3.0",
"webpack": "^5.89.0"
"webpack": "^5.74.0"
},
"engines": {
"node": ">= 18"
"node": "14.* || >= 16"
},
"ember": {
"edition": "octane"
},
"pnpm": {
"overrides": {
"@fleetbase/fleetops-data": "^0.1.7",
"@fleetbase/ember-core": "^0.2.0",
"@fleetbase/ember-ui": "^0.2.9"
"@fleetbase/fleetops-data": "^0.1.5",
"@fleetbase/ember-core": "^0.1.8",
"@fleetbase/ember-ui": "^0.2.6"
}
},
"prettier": {

5898
console/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,17 +5,6 @@ const recast = require('recast');
const babelParser = require('recast/parsers/babel');
const builders = recast.types.builders;
function getExtensionMountPath(extensionName) {
let extensionNameSegments = extensionName.split('/');
let mountName = extensionNameSegments[1];
if (typeof mountName !== 'string') {
mountName = extensionNameSegments[0];
}
return mountName.replace('-engine', '');
}
function only(subject, props = []) {
const keys = Object.keys(subject);
const result = {};
@@ -96,7 +85,8 @@ function getRouterFileContents() {
if (functionExpression) {
// Check and add the new engine mounts
extensions.forEach((extension) => {
const mountPath = getExtensionMountPath(extension.name);
const mountName = extension.name.split('/')[1];
const mountPath = mountName.replace('-engine', '');
let route = mountPath;
if (extension.fleetbase && extension.fleetbase.route) {

View File

@@ -11,7 +11,6 @@ Router.map(function () {
this.route('login', { path: '/' });
this.route('forgot-password');
this.route('reset-password');
this.route('verification');
});
this.route('onboard', function () {
this.route('verify-email');

View File

@@ -1,6 +1,6 @@
import { setupApplicationTest as upstreamSetupApplicationTest, setupRenderingTest as upstreamSetupRenderingTest, setupTest as upstreamSetupTest } from 'ember-qunit';
// This file exists to provide wrappers around ember-qunit's
// This file exists to provide wrappers around ember-qunit's / ember-mocha's
// test setup functions. This way, you can easily extend the setup that is
// needed per test type.

View File

@@ -1,12 +0,0 @@
import { module, test } from 'qunit';
import { setupTest } from '@fleetbase/console/tests/helpers';
module('Unit | Controller | auth/verification', function (hooks) {
setupTest(hooks);
// TODO: Replace this with your real tests.
test('it exists', function (assert) {
let controller = this.owner.lookup('controller:auth/verification');
assert.ok(controller);
});
});

View File

@@ -1,11 +0,0 @@
import { module, test } from 'qunit';
import { setupTest } from '@fleetbase/console/tests/helpers';
module('Unit | Route | auth/verification', function (hooks) {
setupTest(hooks);
test('it exists', function (assert) {
let route = this.owner.lookup('route:auth/verification');
assert.ok(route);
});
});

View File

@@ -24,10 +24,7 @@ terms:
logout: Logout
dashboard: Dashboard
search: Search
search-input: Search Input
common:
search: Search
phrases:
search-input: Search Input

0
console/vendor/.gitkeep vendored Normal file
View File

View File

@@ -2,7 +2,6 @@
variable "REGISTRY" { default = "" }
variable "VERSION" { default = "latest" }
variable "CACHE" { default = "" }
variable "GCP" { default = false }
group "default" {
targets = ["app", "app-httpd"]
@@ -24,7 +23,7 @@ target "app" {
]
tags = notequal("", REGISTRY) ? formatlist(
GCP ? "${REGISTRY}/${tgt}:%s" : "${REGISTRY}:${tgt}-%s",
"${REGISTRY}:${tgt}-%s",
compact(["latest", VERSION])
) : []
@@ -44,7 +43,7 @@ target "app-httpd" {
]
tags = notequal("", REGISTRY) ? formatlist(
GCP ? "${REGISTRY}/app-httpd:%s" : "${REGISTRY}:app-httpd-%s",
"${REGISTRY}:app-httpd-%s",
compact(["latest", VERSION])
) : []
}

View File

@@ -15,6 +15,14 @@ services:
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
MYSQL_DATABASE: "fleetbase"
solid:
image: solidproject/community-server:7
command: ["--baseUrl", "http://solid:3000"]
ports:
- "3000:3000"
volumes:
- ./solid/data:/data
socket:
image: socketcluster/socketcluster:v17.4.0
ports:
@@ -50,7 +58,6 @@ services:
SESSION_DOMAIN: localhost
BROADCAST_DRIVER: socketcluster
MAIL_FROM_NAME: Fleetbase
APP_NAME: Fleetbase
LOG_CHANNEL: daily
depends_on:
- database

View File

@@ -1,23 +0,0 @@
# Patterns to ignore when building packages.
# This supports shell glob matching, relative path matching, and
# negation (prefixed with !). Only one pattern per line.
.DS_Store
# Common VCS dirs
.git/
.gitignore
.bzr/
.bzrignore
.hg/
.hgignore
.svn/
# Common backup files
*.swp
*.bak
*.tmp
*.orig
*~
# Various IDEs
.project
.idea/
*.tmproj
.vscode/

View File

@@ -1,24 +0,0 @@
apiVersion: v2
name: app
description: A Helm chart for Kubernetes
# A chart can be either an 'application' or a 'library' chart.
#
# Application charts are a collection of templates that can be packaged into versioned archives
# to be deployed.
#
# Library charts provide useful utilities or functions for the chart developer. They're included as
# a dependency of application charts to inject those utilities and functions into the rendering
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.1.0
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "1.16.0"

View File

@@ -1 +0,0 @@
Oyster-Upstart-Acronym6

View File

@@ -1 +0,0 @@
DATABASE_URL=mysql://root:Oyster-Upstart-Acronym6@10.87.32.3/fleetbase

View File

@@ -1,3 +0,0 @@
1. Get the application URL by running these commands:
https://{{ .Values.api_host }}

View File

@@ -1,62 +0,0 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "helm.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "helm.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "helm.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "helm.labels" -}}
helm.sh/chart: {{ include "helm.chart" . }}
{{ include "helm.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "helm.selectorLabels" -}}
app.kubernetes.io/name: {{ include "helm.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "helm.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "helm.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -1,17 +0,0 @@
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
name: managed-cert
spec:
domains:
- {{ .Values.api_host }}
- {{ .Values.socketcluster_host }}
---
apiVersion: networking.gke.io/v1beta1
kind: FrontendConfig
metadata:
name: httpsredirect
spec:
redirectToHttps:
enabled: true
responseCodeName: MOVED_PERMANENTLY_DEFAULT

View File

@@ -1,81 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "helm.fullname" . }}
labels:
{{- include "helm.labels" . | nindent 4 }}
spec:
{{- if not .Values.autoscaling.enabled }}
replicas: {{ .Values.replicaCount }}
{{- end }}
selector:
matchLabels:
{{- include "helm.selectorLabels" . | nindent 6 }}
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
labels:
{{- include "helm.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "helm.serviceAccountName" . }}
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: {{ .Chart.Name }}-httpd
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}/app-httpd:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
env:
- name: NGINX_APPLICATION_HOSTNAME
value: localhost
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
livenessProbe:
httpGet:
path: /
port: http
readinessProbe:
httpGet:
path: /
port: http
resources:
{{- toYaml .Values.resources | nindent 12 }}
- name: {{ .Chart.Name }}
securityContext:
{{- toYaml .Values.securityContext | nindent 12 }}
image: "{{ .Values.image.repository }}/app:{{ .Values.image.tag | default .Chart.AppVersion }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
envFrom:
- secretRef:
name: infra-provided-secret
env:
- name: CACHE_URL
value: $(REDIS_SERVICE_PORT)/1
- name: SOCKETCLUSTER_PORT
value: "8000"
- name: SOCKETCLUSTER_HOST
value: $(SOCKETCLUSTER_SERVICE_HOST)
resources:
{{- toYaml .Values.resources | nindent 12 }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -1,28 +0,0 @@
{{- if .Values.autoscaling.enabled }}
apiVersion: autoscaling/v2beta1
kind: HorizontalPodAutoscaler
metadata:
name: {{ include "helm.fullname" . }}
labels:
{{- include "helm.labels" . | nindent 4 }}
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: {{ include "helm.fullname" . }}
minReplicas: {{ .Values.autoscaling.minReplicas }}
maxReplicas: {{ .Values.autoscaling.maxReplicas }}
metrics:
{{- if .Values.autoscaling.targetCPUUtilizationPercentage }}
- type: Resource
resource:
name: cpu
targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }}
{{- end }}
{{- if .Values.autoscaling.targetMemoryUtilizationPercentage }}
- type: Resource
resource:
name: memory
targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }}
{{- end }}
{{- end }}

View File

@@ -1,60 +0,0 @@
{{- if .Values.ingress.enabled -}}
{{- $fullName := include "helm.fullname" . -}}
{{- $svcPort := .Values.service.port -}}
{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }}
{{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }}
{{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}}
{{- end }}
{{- end }}
{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1
{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}}
apiVersion: networking.k8s.io/v1beta1
{{- else -}}
apiVersion: extensions/v1beta1
{{- end }}
kind: Ingress
metadata:
name: {{ $fullName }}
labels:
{{- include "helm.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
{{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }}
ingressClassName: {{ .Values.ingress.className }}
{{- end }}
{{- if .Values.ingress.tls }}
tls:
{{- range .Values.ingress.tls }}
- hosts:
{{- range .hosts }}
- {{ . | quote }}
{{- end }}
secretName: {{ .secretName }}
{{- end }}
{{- end }}
rules:
- host: {{ .Values.api_host }}
http:
paths:
- path: /*
pathType: ImplementationSpecific
backend:
service:
name: fleetbase-app
port:
number: {{ $svcPort }}
- host: {{ .Values.socketcluster_host }}
http:
paths:
- path: /*
pathType: ImplementationSpecific
backend:
service:
name: socketcluster
port:
number: {{ $svcPort }}
{{- end }}

View File

@@ -1,33 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:6-alpine # Use the Redis Docker image
ports:
- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: redis-service
spec:
selector:
app: redis
ports:
- protocol: TCP
port: 6379
targetPort: 6379
type: ClusterIP

View File

@@ -1,15 +0,0 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "helm.fullname" . }}
labels:
{{- include "helm.labels" . | nindent 4 }}
spec:
type: {{ .Values.service.type }}
ports:
- port: {{ .Values.service.port }}
targetPort: http
protocol: TCP
name: http
selector:
{{- include "helm.selectorLabels" . | nindent 4 }}

View File

@@ -1,12 +0,0 @@
{{- if .Values.serviceAccount.create -}}
apiVersion: v1
kind: ServiceAccount
metadata:
name: {{ include "helm.serviceAccountName" . }}
labels:
{{- include "helm.labels" . | nindent 4 }}
{{- with .Values.serviceAccount.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
{{- end }}

View File

@@ -1,36 +0,0 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: socketcluster
spec:
replicas: 1
selector:
matchLabels:
app: socketcluster
template:
metadata:
labels:
app: socketcluster
spec:
containers:
- name: socketcluster
image: socketcluster/socketcluster:v19.1.2
ports:
- containerPort: 8000
env:
- name: SOCKETCLUSTER_PORT
value: "8000"
---
apiVersion: v1
kind: Service
metadata:
name: socketcluster
spec:
selector:
app: socketcluster
ports:
- protocol: TCP
port: 80
targetPort: 8000
type: ClusterIP

View File

@@ -1,15 +0,0 @@
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "helm.fullname" . }}-test-connection"
labels:
{{- include "helm.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "helm.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never

View File

@@ -1,80 +0,0 @@
# Default values for helm.
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: OVERRIDE
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: "latest"
imagePullSecrets: []
nameOverride: ""
fullnameOverride: ""
serviceAccount:
# Specifies whether a service account should be created
create: true
# Annotations to add to the service account
annotations: {}
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext: {}
# fsGroup: 2000
securityContext: {}
# capabilities:
# drop:
# - ALL
# readOnlyRootFilesystem: true
# runAsNonRoot: true
# runAsUser: 1000
service:
type: ClusterIP
port: 80
ingress:
enabled: true
className: ""
annotations:
kubernetes.io/ingress.class: gce
kubernetes.io/ingress.global-static-ip-name: OVERRIDE
networking.gke.io/managed-certificates: managed-cert
networking.gke.io/v1beta1.FrontendConfig: "httpsredirect"
tls: {}
# - secretName: chart-example-tls
# hosts:
# - chart-example.local
resources: {}
# We usually recommend not to specify default resources and to leave this as a conscious
# choice for the user. This also increases chances charts run on environments with little
# resources, such as Minikube. If you do want to specify resources, uncomment the following
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
# limits:
# cpu: 100m
# memory: 128Mi
# requests:
# cpu: 100m
# memory: 128Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 100
targetCPUUtilizationPercentage: 80
# targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}

1
packages/solid Submodule

Submodule packages/solid added at dc2ee3a0ec

View File

@@ -1,55 +0,0 @@
#!/bin/bash
# Find the root directory of your repository
root_dir=$(git rev-parse --show-toplevel)
# Check if the packages directory exists
packages_dir="$root_dir/packages"
if [ ! -d "$packages_dir" ]; then
echo "Packages directory not found."
exit 1
fi
# Initialize flags
remove_lock=false
remove_modules=false
for arg in "$@"
do
case $arg in
--remove-lock)
remove_lock=true
;;
--remove-modules)
remove_modules=true
;;
esac
done
# Navigate to the packages directory
cd "$packages_dir"
# Find all child directories and run pnpm install if package.json exists
for dir in */; do
if [[ -f "${dir}package.json" ]]; then
echo "Running pnpm install in $dir"
# Remove pnpm-lock.yaml if the option is set
if [ "$remove_lock" = true ] && [ -f "${dir}pnpm-lock.yaml" ]; then
echo "Removing pnpm-lock.yaml in $dir"
rm "${dir}pnpm-lock.yaml"
fi
# Remove ./node_modules if the option is set
if [ "$remove_modules" = true ] && [ -d "${dir}node_modules" ]; then
echo "Removing /node_modules in $dir"
rm -rf "${dir}node_modules"
fi
cd "$dir"
pnpm install
cd "$packages_dir" # Go back to the packages directory
else
echo "No package.json found in $dir, skipping..."
fi
done