WIP Items module.

This commit is contained in:
Ahmed Bouhuolia
2019-09-03 02:07:28 +02:00
parent cb8c294d74
commit 70809cb05c
142 changed files with 12674 additions and 64 deletions

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/data

29
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,29 @@
{
"eslint.validate": [
{
"language":"vue",
"autoFix":true
},
{
"language":"html",
"autoFix":true
},
{
"language":"javascript",
"autoFix":true
},
{
"language":"typescript",
"autoFix":true
}
],
"eslint.alwaysShowStatus": true,
"eslint.workingDirectories": [
"./client",
"./server"
],
"editor.useTabStops": false,
"editor.tabSize": 2,
"editor.insertSpaces": true,
}

View File

@@ -8,7 +8,18 @@
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"],
"plugins": [
"transform-vue-jsx",
"transform-runtime",
[
"component",
{
"libraryName": "element-ui",
"styleLibraryName": "~node_modules/element-theme-chalk/src",
"ext": ".scss"
}
]
],
"env": {
"test": {
"presets": ["env", "stage-2"],

View File

@@ -38,6 +38,7 @@ module.exports = {
props: true,
ignorePropertyModificationsFor: [
'state', // for vuex state
's',
'acc', // for reduce accumulators
'e' // for e.returnvalue
]

View File

@@ -21,7 +21,7 @@ module.exports = {
// If true, your code will be linted during bundling and
// linting errors and warnings will be shown in the console.
useEslint: true,
useEslint: false,
// If true, eslint errors and warnings will also be shown in the error overlay
// in the browser.
showEslintErrorsInOverlay: false,

View File

@@ -4,6 +4,35 @@
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"@babel/helper-module-imports": {
"version": "7.0.0-beta.35",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.0.0-beta.35.tgz",
"integrity": "sha512-vaC1KyIZSuyWb3Lj277fX0pxivyHwuDU4xZsofqgYAbkDxNieMg2vuhzP5AgMweMY7fCQUMTi+BgPqTLjkxXFg==",
"dev": true,
"requires": {
"@babel/types": "7.0.0-beta.35",
"lodash": "^4.2.0"
}
},
"@babel/types": {
"version": "7.0.0-beta.35",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.0.0-beta.35.tgz",
"integrity": "sha512-y9XT11CozHDgjWcTdxmhSj13rJVXpa5ZXwjjOiTedjaM0ba5ItqdS02t31EhPl7HtOWxsZkYCCUNrSfrOisA6w==",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"lodash": "^4.2.0",
"to-fast-properties": "^2.0.0"
},
"dependencies": {
"to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
"dev": true
}
}
},
"@sinonjs/commons": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.4.0.tgz",
@@ -807,6 +836,15 @@
"babel-runtime": "^6.22.0"
}
},
"babel-plugin-component": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/babel-plugin-component/-/babel-plugin-component-1.1.1.tgz",
"integrity": "sha512-WUw887kJf2GH80Ng/ZMctKZ511iamHNqPhd9uKo14yzisvV7Wt1EckIrb8oq/uCz3B3PpAW7Xfl7AkTLDYT6ag==",
"dev": true,
"requires": {
"@babel/helper-module-imports": "7.0.0-beta.35"
}
},
"babel-plugin-istanbul": {
"version": "4.1.6",
"resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz",
@@ -4058,6 +4096,11 @@
"integrity": "sha512-xNabEDbMIKPLQd6xgv4nyyeMaWXIKSJr6G51ZhUemHhbz6kjZAYcygA8CvfEcMF+Mt5eLmDWaLmfSOWdQxzBVQ==",
"dev": true
},
"element-theme-chalk": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/element-theme-chalk/-/element-theme-chalk-2.12.0.tgz",
"integrity": "sha512-q0G2yJErNfiHC+gz/PtqCnufcLqZmYN1c9kHNzStlN6z45CFbRYkTXLnMkj/8G+T3LlaYqoRFvWy5NMq4ltF2A=="
},
"element-ui": {
"version": "2.11.1",
"resolved": "https://registry.npmjs.org/element-ui/-/element-ui-2.11.1.tgz",

View File

@@ -20,7 +20,8 @@
"vue-axios": "^2.1.4",
"vue-router": "^3.0.1",
"vuex": "^3.1.1",
"vuex-i18n": "^1.13.1"
"vuex-i18n": "^1.13.1",
"element-theme-chalk": "^2.12.0"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
@@ -28,6 +29,7 @@
"babel-eslint": "^7.2.3",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.1",
"babel-plugin-component": "^1.1.1",
"babel-plugin-istanbul": "^4.1.1",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.22.0",

View File

@@ -9,3 +9,11 @@ export default {
name: 'App',
};
</script>
<style lang="scss">
@import 'style/style.scss';
body{
margin: 0;
}
</style>

View File

@@ -1,5 +1,5 @@
import Vue from 'vue';
import ElementUI from 'element-ui';
import {Form, FormItem, Input} from 'element-ui';
import App from '@/App';
import router from '@/router';
import store from '@/store';
@@ -9,7 +9,9 @@ import '@/plugins/i18n';
Vue.config.productionTip = false;
Vue.use(ElementUI);
Vue.use(Form);
Vue.use(FormItem);
Vue.use(Input);
const app = new Vue({
el: '#app',

View File

@@ -0,0 +1,46 @@
<template>
<div id="reset-password">
<el-form ref="form" class="form-container">
<el-form-item :label="$t('username_password')" prop="title">
<el-input v-model="form.crediential" :maxlength="100" name="name" required />
</el-form-item>
</el-form>
<el-button type="primary">{{ $t('reset_password') }}</el-button>
<div class="form-note">
<router-link :to="{name: 'login'}">{{ $t('return_to_login') }}</router-link>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex';
export default {
name: 'forget-password',
data() {
return {
form: {
crediential: '',
},
};
},
methods: {
...mapActions(['login']),
/**
* Handle the submitting the reset password form.
*/
onSubmit() {
const form = {};
this.sendResetPassword({ form }).then(() => {
// console.log(response);
}).catch(() => {
// console.log(error);
});
},
},
};
</script>

View File

@@ -10,12 +10,23 @@
</el-form-item>
<el-button type="primary">{{ $t('login') }}</el-button>
<el-link>{{ $t('forget_your_password') }}</el-link>
</el-form>
<div class="form-note">
<div class="forget-password">
<router-link :to="{name: 'forgetPassword'}">{{ $t('forget_password') }}</router-link>
</div>
</div>
</div>
</template>
<script>
import { mapActions } from 'vuex';
const STATE = {
USER_NOT_ACTIVE: 1,
};
export default {
name: 'login',
data() {
@@ -27,6 +38,25 @@ export default {
};
},
methods: {
...mapActions(['login']),
/**
* Handle the submitting the login form.
*/
onSubmit() {
const form = {};
this.login({ form }).then(() => {
}).catch((error) => {
const { response } = error;
const { data } = response;
if (data.error.type === 'USER_NOT_ACTIVE') {
this.current_state = STATE.USER_NOT_ACTIVE;
}
});
},
},
};
</script>

View File

@@ -1,4 +1,79 @@
<template>
<div class="reset-password" id="reset-password">
<el-form ref="form" class="form-container">
<el-form-item :label="$t('password')" prop="title">
<el-input v-model="form.password" :maxlength="100" name="password" required />
</el-form-item>
<el-form-item :label="$t('confirm_password')">
<el-input v-model="form.confirm_password" name="confirm_password" />
</el-form-item>
</template>
<el-button @click.prevent="onSubmit" type="primary">
{{ $t('reset_the_password') }}
</el-button>
<el-link>{{ $t('forget_your_password') }}</el-link>
</el-form>
</div>
</template>
<script>
import { mapActions } from 'vuex';
const STATE = {
SUCCESS: 1,
TOKEN_INVALID: 2,
};
export default {
name: 'reset-password',
data() {
return {
form: {
password: '',
confirm_password: '',
},
};
},
props: {
token: {
required: true,
type: String,
},
},
computed: {
isSuccess() {
return this.current_state === STATE.SUCCESS;
},
isTokenInvalid() {
return this.current_state === STATE.TOKEN_INVALID;
},
},
methods: {
...mapActions(['newPassword']),
/**
* Handle the submitting the reset password form.
*/
onSubmit() {
const form = {
...this.form,
token: this.token,
};
this.newPassword({ form }).then(() => {
this.$router.push({ name: 'login' });
}).catch((error) => {
const { response } = error;
const { data } = response;
if (data.error.type === 'token.invalid') {
this.current_status = STATE.TOKEN_INVALID;
}
});
},
},
};
</script>

View File

@@ -0,0 +1,31 @@
<template>
</template>
<script>
const STATE = {
LOADING: 1,
EMPTY_LIST: 2,
NO_SEARCH_RESULTS: 3,
};
export default {
name: 'customers-list',
data() {
return {
current_state: 0,
};
},
computed: {
loading() {
return this.current_state === STATE.LOADING;
},
emptyList() {
return this.current_state === STATE.EMPTY_LIST;
},
noSearchResults() {
return this.current_state === STATE.NO_SEARCH_RESULTS;
},
},
}
</script>

View File

@@ -26,8 +26,12 @@ export default {
</script>
<style lang="scss">
#dashboard{
.dashboard{
display: flex;
flex-direction: row;
&__content{
width: 100%;
}
}
</style>

View File

@@ -0,0 +1,3 @@
<template>
</template>

View File

@@ -0,0 +1,9 @@
<template>
</template>
<script>
export default {
name: 'products-list',
}
</script>

View File

@@ -7,11 +7,17 @@ const routes = [
'@/pages/Auth/Auth.vue'),
children: [
{
name: 'authLogin',
name: 'login',
path: '/login',
component: () => import(/* webpackChunkName: "login" */
'@/pages/Auth/Login.vue'),
},
{
name: 'forgetPassword',
path: '/forget_password',
component: () => import(/* webpackChunkName: "forget_password" */
'@/pages/Auth/ForgetPassword.vue'),
},
],
},
{

View File

@@ -3,6 +3,8 @@ import Vuex from 'vuex';
import vuexI18n from 'vuex-i18n';
import sidebar from '@/store/modules/sidebar';
import app from '@/store/modules/app';
import items from '@/store/modules/items';
import customers from '@/store/modules/customers';
const debug = process.env.NODE_ENV !== 'production';
@@ -12,6 +14,8 @@ const store = new Vuex.Store({
modules: {
app,
sidebar,
items,
customers,
},
strict: debug,
});

View File

@@ -1,9 +1,9 @@
import ApiService from '~/plugins/api-service';
import ApiService from '@/plugins/api-service';
let state = {
const state = {
token: localStorage.getItem('token') || '',
errors: {},
role: {}
role: {},
};
const getters = {
@@ -14,9 +14,9 @@ const getters = {
const actions = {
/**
* User login authentication request.
* User login login authentication request.
*/
async authRequest({ commit }, { form }) {
async login({ commit }, { form }) {
const response = await ApiService.post('auth/login', form);
const { data } = response;
@@ -27,32 +27,29 @@ const actions = {
},
/**
* Send reset password email or SMS.
* Send reset password via email or SMS.
*/
sendResetPassword({}, { email }) {
return ApiService.post('auth/send_reset_password', { email });
},
/**
* Verify reset password verification code.
*/
verifyResetPasswordToken({ commit, dispatch }, { token }) {
return ApiService.post(`reset/${token}`);
}
newPassword(, { form }) {
return ApiService.post('auth/new_password', form);
},
};
const mutations = {
setToken(state, token) {
setToken(s, token) {
localStorage.setItem('token', token);
state.token = token;
s.token = token;
},
removeToken(state) {
removeToken(s) {
localStorage.removeItem('token');
state.token = '';
s.token = '';
},
};
export default {state, actions, mutations, getters};
export default { state, actions, mutations, getters };

View File

@@ -0,0 +1,77 @@
import ApiService from '@/plugins/api-service';
const state = {
list: {},
details: [],
};
const getters = {
getCustomers: s => s.list,
getCustomer: s => id => s.details.find(i => i.id === id),
};
const actions = {
/**
* Fetches customers with current page.
*/
async fetchCustomers({ commit }, { query } = {}) {
const response = await ApiService.post('customers', { params: query });
const { data } = response;
const { count } = data.pagination;
commit('setItems', data);
if (count) {
commit('setSidebarItemCount', {
name: 'customers', count,
});
}
return data;
},
/**
* Fetch the given customer details.
*/
async fetchCustomer({ commit }, { id }) {
const response = await ApiService.get(`customers/${id}`);
const { data } = response;
commit('setItem', data);
return data;
},
/**
* Delete the given customer.
*/
async deleteCustomer({}, { id }) {
return ApiService.delete(`customers/${id}`);
},
/**
* Submit the new customer.
*/
async newCustomer({}, { form }) {
return ApiService.post('customers', form);
},
/**
* Update details the given customer.
*/
async updateCustomer({}, { form, id }) {
return ApiService.post(`customers/${id}`, form);
},
};
const mutations = {
setCustomers(s, items) {
s.list = items;
},
setCustomer(s, item) {
s.details = s.details.filter(i => i.id !== item.id);
s.details.push(item);
},
};
export default { state, actions, mutations, getters };

View File

@@ -0,0 +1,71 @@
import ApiService from '@/plugins/api-service';
const state = {
list: {},
details: [],
};
const getters = {
getItems: s => s.list,
getItem: s => id => s.details.find(i => i.id === id),
};
const actions = {
/**
* Fetches the products/services list.
*/
async fetchItems({ commit }, { query } = {}) {
const response = await ApiService.post('items', { params: query });
const { data } = response;
const { count } = data.pagination;
commit('setItems', data);
if (count) {
commit('setSidebarItemCount', {
name: 'items', count,
});
}
return data;
},
/**
* Fetch the given product/service details.
*/
async fetchItem({ commit }, { id }) {
const response = await ApiService.get(`${id}`);
const { data } = response;
commit('setItem', data);
return data;
},
/**
* Delete the given product/service.
*/
async deleteItem({ commit }, { id }) {
return ApiService.delete(`items/${id}`);
},
async newItem({}, { form }) {
return ApiService.post('items', form);
},
async updateItem({}, { form, id }) {
return ApiService.post(`items/${id}`, form);
},
};
const mutations = {
setItems(s, items) {
s.list = items;
},
setItem(s, item) {
s.details = s.details.filter(i => i.id !== item.id);
s.details.push(item);
},
};
export default { state, actions, mutations, getters };

View File

@@ -37,12 +37,15 @@ const actions = {
* Set count to the given sidebar item.
*/
setSidebarItemCount(s, { name, count }) {
s.items.forEach((item) => {
s.items = s.items.map((item) => {
const mapped = { ...item };
if (item.name === name) {
item.count = count;
mapped.count = count;
}
})
}
}
return mapped;
});
},
};
export default { state, getters, actions };

View File

@@ -0,0 +1,994 @@
/* Element Chalk Variables */
// Special comment for theme configurator
// type|skipAutoTranslation|Category|Order
// skipAutoTranslation 1
/* Transition
-------------------------- */
$--all-transition: all .3s cubic-bezier(.645,.045,.355,1) !default;
$--fade-transition: opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default;
$--fade-linear-transition: opacity 200ms linear !default;
$--md-fade-transition: transform 300ms cubic-bezier(0.23, 1, 0.32, 1), opacity 300ms cubic-bezier(0.23, 1, 0.32, 1) !default;
$--border-transition-base: border-color .2s cubic-bezier(.645,.045,.355,1) !default;
$--color-transition-base: color .2s cubic-bezier(.645,.045,.355,1) !default;
/* Color
-------------------------- */
/// color|1|Brand Color|0
$--color-primary: #409EFF !default;
/// color|1|Background Color|4
$--color-white: #FFFFFF !default;
/// color|1|Background Color|4
$--color-black: #000000 !default;
$--color-primary-light-1: mix($--color-white, $--color-primary, 10%) !default; /* 53a8ff */
$--color-primary-light-2: mix($--color-white, $--color-primary, 20%) !default; /* 66b1ff */
$--color-primary-light-3: mix($--color-white, $--color-primary, 30%) !default; /* 79bbff */
$--color-primary-light-4: mix($--color-white, $--color-primary, 40%) !default; /* 8cc5ff */
$--color-primary-light-5: mix($--color-white, $--color-primary, 50%) !default; /* a0cfff */
$--color-primary-light-6: mix($--color-white, $--color-primary, 60%) !default; /* b3d8ff */
$--color-primary-light-7: mix($--color-white, $--color-primary, 70%) !default; /* c6e2ff */
$--color-primary-light-8: mix($--color-white, $--color-primary, 80%) !default; /* d9ecff */
$--color-primary-light-9: mix($--color-white, $--color-primary, 90%) !default; /* ecf5ff */
/// color|1|Functional Color|1
$--color-success: #67C23A !default;
/// color|1|Functional Color|1
$--color-warning: #E6A23C !default;
/// color|1|Functional Color|1
$--color-danger: #F56C6C !default;
/// color|1|Functional Color|1
$--color-info: #909399 !default;
$--color-success-light: mix($--color-white, $--color-success, 80%) !default;
$--color-warning-light: mix($--color-white, $--color-warning, 80%) !default;
$--color-danger-light: mix($--color-white, $--color-danger, 80%) !default;
$--color-info-light: mix($--color-white, $--color-info, 80%) !default;
$--color-success-lighter: mix($--color-white, $--color-success, 90%) !default;
$--color-warning-lighter: mix($--color-white, $--color-warning, 90%) !default;
$--color-danger-lighter: mix($--color-white, $--color-danger, 90%) !default;
$--color-info-lighter: mix($--color-white, $--color-info, 90%) !default;
/// color|1|Font Color|2
$--color-text-primary: #303133 !default;
/// color|1|Font Color|2
$--color-text-regular: #606266 !default;
/// color|1|Font Color|2
$--color-text-secondary: #909399 !default;
/// color|1|Font Color|2
$--color-text-placeholder: #C0C4CC !default;
/// color|1|Border Color|3
$--border-color-base: #DCDFE6 !default;
/// color|1|Border Color|3
$--border-color-light: #E4E7ED !default;
/// color|1|Border Color|3
$--border-color-lighter: #EBEEF5 !default;
/// color|1|Border Color|3
$--border-color-extra-light: #F2F6FC !default;
// Background
/// color|1|Background Color|4
$--background-color-base: #F5F7FA !default;
/* Link
-------------------------- */
$--link-color: $--color-primary-light-2 !default;
$--link-hover-color: $--color-primary !default;
/* Border
-------------------------- */
$--border-width-base: 1px !default;
$--border-style-base: solid !default;
$--border-color-hover: $--color-text-placeholder !default;
$--border-base: $--border-width-base $--border-style-base $--border-color-base !default;
/// borderRadius|1|Radius|0
$--border-radius-base: 4px !default;
/// borderRadius|1|Radius|0
$--border-radius-small: 2px !default;
/// borderRadius|1|Radius|0
$--border-radius-circle: 100% !default;
/// borderRadius|1|Radius|0
$--border-radius-zero: 0 !default;
// Box-shadow
/// boxShadow|1|Shadow|1
$--box-shadow-base: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04) !default;
// boxShadow|1|Shadow|1
$--box-shadow-dark: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .12) !default;
/// boxShadow|1|Shadow|1
$--box-shadow-light: 0 2px 12px 0 rgba(0, 0, 0, 0.1) !default;
/* Fill
-------------------------- */
$--fill-base: $--color-white !default;
/* Typography
-------------------------- */
$--font-path: 'fonts' !default;
$--font-display: 'auto' !default;
/// fontSize|1|Font Size|0
$--font-size-extra-large: 20px !default;
/// fontSize|1|Font Size|0
$--font-size-large: 18px !default;
/// fontSize|1|Font Size|0
$--font-size-medium: 16px !default;
/// fontSize|1|Font Size|0
$--font-size-base: 14px !default;
/// fontSize|1|Font Size|0
$--font-size-small: 13px !default;
/// fontSize|1|Font Size|0
$--font-size-extra-small: 12px !default;
/// fontWeight|1|Font Weight|1
$--font-weight-primary: 500 !default;
/// fontWeight|1|Font Weight|1
$--font-weight-secondary: 100 !default;
/// fontLineHeight|1|Line Height|2
$--font-line-height-primary: 24px !default;
/// fontLineHeight|1|Line Height|2
$--font-line-height-secondary: 16px !default;
$--font-color-disabled-base: #bbb !default;
/* Size
-------------------------- */
$--size-base: 14px !default;
/* z-index
-------------------------- */
$--index-normal: 1 !default;
$--index-top: 1000 !default;
$--index-popper: 2000 !default;
/* Disable base
-------------------------- */
$--disabled-fill-base: $--background-color-base !default;
$--disabled-color-base: $--color-text-placeholder !default;
$--disabled-border-base: $--border-color-light !default;
/* Icon
-------------------------- */
$--icon-color: #666 !default;
$--icon-color-base: $--color-info !default;
/* Checkbox
-------------------------- */
/// fontSize||Font|1
$--checkbox-font-size: 14px !default;
/// fontWeight||Font|1
$--checkbox-font-weight: $--font-weight-primary !default;
/// color||Color|0
$--checkbox-font-color: $--color-text-regular !default;
$--checkbox-input-height: 14px !default;
$--checkbox-input-width: 14px !default;
/// borderRadius||Border|2
$--checkbox-border-radius: $--border-radius-small !default;
/// color||Color|0
$--checkbox-background-color: $--color-white !default;
$--checkbox-input-border: $--border-base !default;
/// color||Color|0
$--checkbox-disabled-border-color: $--border-color-base !default;
$--checkbox-disabled-input-fill: #edf2fc !default;
$--checkbox-disabled-icon-color: $--color-text-placeholder !default;
$--checkbox-disabled-checked-input-fill: $--border-color-extra-light !default;
$--checkbox-disabled-checked-input-border-color: $--border-color-base !default;
$--checkbox-disabled-checked-icon-color: $--color-text-placeholder !default;
/// color||Color|0
$--checkbox-checked-font-color: $--color-primary !default;
$--checkbox-checked-input-border-color: $--color-primary !default;
/// color||Color|0
$--checkbox-checked-background-color: $--color-primary !default;
$--checkbox-checked-icon-color: $--fill-base !default;
$--checkbox-input-border-color-hover: $--color-primary !default;
/// height||Other|4
$--checkbox-bordered-height: 40px !default;
/// padding||Spacing|3
$--checkbox-bordered-padding: 9px 20px 9px 10px !default;
/// padding||Spacing|3
$--checkbox-bordered-medium-padding: 7px 20px 7px 10px !default;
/// padding||Spacing|3
$--checkbox-bordered-small-padding: 5px 15px 5px 10px !default;
/// padding||Spacing|3
$--checkbox-bordered-mini-padding: 3px 15px 3px 10px !default;
$--checkbox-bordered-medium-input-height: 14px !default;
$--checkbox-bordered-medium-input-width: 14px !default;
/// height||Other|4
$--checkbox-bordered-medium-height: 36px !default;
$--checkbox-bordered-small-input-height: 12px !default;
$--checkbox-bordered-small-input-width: 12px !default;
/// height||Other|4
$--checkbox-bordered-small-height: 32px !default;
$--checkbox-bordered-mini-input-height: 12px !default;
$--checkbox-bordered-mini-input-width: 12px !default;
/// height||Other|4
$--checkbox-bordered-mini-height: 28px !default;
/// color||Color|0
$--checkbox-button-checked-background-color: $--color-primary !default;
/// color||Color|0
$--checkbox-button-checked-font-color: $--color-white !default;
/// color||Color|0
$--checkbox-button-checked-border-color: $--color-primary !default;
/* Radio
-------------------------- */
/// fontSize||Font|1
$--radio-font-size: $--font-size-base !default;
/// fontWeight||Font|1
$--radio-font-weight: $--font-weight-primary !default;
/// color||Color|0
$--radio-font-color: $--color-text-regular !default;
$--radio-input-height: 14px !default;
$--radio-input-width: 14px !default;
/// borderRadius||Border|2
$--radio-input-border-radius: $--border-radius-circle !default;
/// color||Color|0
$--radio-input-background-color: $--color-white !default;
$--radio-input-border: $--border-base !default;
/// color||Color|0
$--radio-input-border-color: $--border-color-base !default;
/// color||Color|0
$--radio-icon-color: $--color-white !default;
$--radio-disabled-input-border-color: $--disabled-border-base !default;
$--radio-disabled-input-fill: $--disabled-fill-base !default;
$--radio-disabled-icon-color: $--disabled-fill-base !default;
$--radio-disabled-checked-input-border-color: $--disabled-border-base !default;
$--radio-disabled-checked-input-fill: $--disabled-fill-base !default;
$--radio-disabled-checked-icon-color: $--color-text-placeholder !default;
/// color||Color|0
$--radio-checked-font-color: $--color-primary !default;
/// color||Color|0
$--radio-checked-input-border-color: $--color-primary !default;
/// color||Color|0
$--radio-checked-input-background-color: $--color-white !default;
/// color||Color|0
$--radio-checked-icon-color: $--color-primary !default;
$--radio-input-border-color-hover: $--color-primary !default;
$--radio-bordered-height: 40px !default;
$--radio-bordered-padding: 12px 20px 0 10px !default;
$--radio-bordered-medium-padding: 10px 20px 0 10px !default;
$--radio-bordered-small-padding: 8px 15px 0 10px !default;
$--radio-bordered-mini-padding: 6px 15px 0 10px !default;
$--radio-bordered-medium-input-height: 14px !default;
$--radio-bordered-medium-input-width: 14px !default;
$--radio-bordered-medium-height: 36px !default;
$--radio-bordered-small-input-height: 12px !default;
$--radio-bordered-small-input-width: 12px !default;
$--radio-bordered-small-height: 32px !default;
$--radio-bordered-mini-input-height: 12px !default;
$--radio-bordered-mini-input-width: 12px !default;
$--radio-bordered-mini-height: 28px !default;
/// fontSize||Font|1
$--radio-button-font-size: $--font-size-base !default;
/// color||Color|0
$--radio-button-checked-background-color: $--color-primary !default;
/// color||Color|0
$--radio-button-checked-font-color: $--color-white !default;
/// color||Color|0
$--radio-button-checked-border-color: $--color-primary !default;
$--radio-button-disabled-checked-fill: $--border-color-extra-light !default;
/* Select
-------------------------- */
$--select-border-color-hover: $--border-color-hover !default;
$--select-disabled-border: $--disabled-border-base !default;
/// fontSize||Font|1
$--select-font-size: $--font-size-base !default;
$--select-close-hover-color: $--color-text-secondary !default;
$--select-input-color: $--color-text-placeholder !default;
$--select-multiple-input-color: #666 !default;
/// color||Color|0
$--select-input-focus-border-color: $--color-primary !default;
/// fontSize||Font|1
$--select-input-font-size: 14px !default;
$--select-option-color: $--color-text-regular !default;
$--select-option-disabled-color: $--color-text-placeholder !default;
$--select-option-disabled-background: $--color-white !default;
/// height||Other|4
$--select-option-height: 34px !default;
$--select-option-hover-background: $--background-color-base !default;
/// color||Color|0
$--select-option-selected-font-color: $--color-primary !default;
$--select-option-selected-hover: $--background-color-base !default;
$--select-group-color: $--color-info !default;
$--select-group-height: 30px !default;
$--select-group-font-size: 12px !default;
$--select-dropdown-background: $--color-white !default;
$--select-dropdown-shadow: $--box-shadow-light !default;
$--select-dropdown-empty-color: #999 !default;
/// height||Other|4
$--select-dropdown-max-height: 274px !default;
$--select-dropdown-padding: 6px 0 !default;
$--select-dropdown-empty-padding: 10px 0 !default;
$--select-dropdown-border: solid 1px $--border-color-light !default;
/* Alert
-------------------------- */
$--alert-padding: 8px 16px !default;
/// borderRadius||Border|2
$--alert-border-radius: $--border-radius-base !default;
/// fontSize||Font|1
$--alert-title-font-size: 13px !default;
/// fontSize||Font|1
$--alert-description-font-size: 12px !default;
/// fontSize||Font|1
$--alert-close-font-size: 12px !default;
/// fontSize||Font|1
$--alert-close-customed-font-size: 13px !default;
$--alert-success-color: $--color-success-lighter !default;
$--alert-info-color: $--color-info-lighter !default;
$--alert-warning-color: $--color-warning-lighter !default;
$--alert-danger-color: $--color-danger-lighter !default;
/// height||Other|4
$--alert-icon-size: 16px !default;
/// height||Other|4
$--alert-icon-large-size: 28px !default;
/* MessageBox
-------------------------- */
/// color||Color|0
$--messagebox-title-color: $--color-text-primary !default;
$--msgbox-width: 420px !default;
$--msgbox-border-radius: 4px !default;
/// fontSize||Font|1
$--messagebox-font-size: $--font-size-large !default;
/// fontSize||Font|1
$--messagebox-content-font-size: $--font-size-base !default;
/// color||Color|0
$--messagebox-content-color: $--color-text-regular !default;
/// fontSize||Font|1
$--messagebox-error-font-size: 12px !default;
$--msgbox-padding-primary: 15px !default;
/// color||Color|0
$--messagebox-success-color: $--color-success !default;
/// color||Color|0
$--messagebox-info-color: $--color-info !default;
/// color||Color|0
$--messagebox-warning-color: $--color-warning !default;
/// color||Color|0
$--messagebox-danger-color: $--color-danger !default;
/* Message
-------------------------- */
$--message-shadow: $--box-shadow-base !default;
$--message-min-width: 380px !default;
$--message-background-color: #edf2fc !default;
$--message-padding: 15px 15px 15px 20px !default;
/// color||Color|0
$--message-close-icon-color: $--color-text-placeholder !default;
/// height||Other|4
$--message-close-size: 16px !default;
/// color||Color|0
$--message-close-hover-color: $--color-text-secondary !default;
/// color||Color|0
$--message-success-font-color: $--color-success !default;
/// color||Color|0
$--message-info-font-color: $--color-info !default;
/// color||Color|0
$--message-warning-font-color: $--color-warning !default;
/// color||Color|0
$--message-danger-font-color: $--color-danger !default;
/* Notification
-------------------------- */
$--notification-width: 330px !default;
/// padding||Spacing|3
$--notification-padding: 14px 26px 14px 13px !default;
$--notification-radius: 8px !default;
$--notification-shadow: $--box-shadow-light !default;
/// color||Color|0
$--notification-border-color: $--border-color-lighter !default;
$--notification-icon-size: 24px !default;
$--notification-close-font-size: $--message-close-size !default;
$--notification-group-margin-left: 13px !default;
$--notification-group-margin-right: 8px !default;
/// fontSize||Font|1
$--notification-content-font-size: $--font-size-base !default;
/// color||Color|0
$--notification-content-color: $--color-text-regular !default;
/// fontSize||Font|1
$--notification-title-font-size: 16px !default;
/// color||Color|0
$--notification-title-color: $--color-text-primary !default;
/// color||Color|0
$--notification-close-color: $--color-text-secondary !default;
/// color||Color|0
$--notification-close-hover-color: $--color-text-regular !default;
/// color||Color|0
$--notification-success-icon-color: $--color-success !default;
/// color||Color|0
$--notification-info-icon-color: $--color-info !default;
/// color||Color|0
$--notification-warning-icon-color: $--color-warning !default;
/// color||Color|0
$--notification-danger-icon-color: $--color-danger !default;
/* Input
-------------------------- */
$--input-font-size: $--font-size-base !default;
/// color||Color|0
$--input-font-color: $--color-text-regular !default;
/// height||Other|4
$--input-width: 140px !default;
/// height||Other|4
$--input-height: 40px !default;
$--input-border: $--border-base !default;
$--input-border-color: $--border-color-base !default;
/// borderRadius||Border|2
$--input-border-radius: $--border-radius-base !default;
$--input-border-color-hover: $--border-color-hover !default;
/// color||Color|0
$--input-background-color: $--color-white !default;
$--input-fill-disabled: $--disabled-fill-base !default;
$--input-color-disabled: $--font-color-disabled-base !default;
/// color||Color|0
$--input-icon-color: $--color-text-placeholder !default;
/// color||Color|0
$--input-placeholder-color: $--color-text-placeholder !default;
$--input-max-width: 314px !default;
$--input-hover-border: $--border-color-hover !default;
$--input-clear-hover-color: $--color-text-secondary !default;
$--input-focus-border: $--color-primary !default;
$--input-focus-fill: $--color-white !default;
$--input-disabled-fill: $--disabled-fill-base !default;
$--input-disabled-border: $--disabled-border-base !default;
$--input-disabled-color: $--disabled-color-base !default;
$--input-disabled-placeholder-color: $--color-text-placeholder !default;
/// fontSize||Font|1
$--input-medium-font-size: 14px !default;
/// height||Other|4
$--input-medium-height: 36px !default;
/// fontSize||Font|1
$--input-small-font-size: 13px !default;
/// height||Other|4
$--input-small-height: 32px !default;
/// fontSize||Font|1
$--input-mini-font-size: 12px !default;
/// height||Other|4
$--input-mini-height: 28px !default;
/* Cascader
-------------------------- */
/// color||Color|0
$--cascader-menu-font-color: $--color-text-regular !default;
/// color||Color|0
$--cascader-menu-selected-font-color: $--color-primary !default;
$--cascader-menu-fill: $--fill-base !default;
$--cascader-menu-font-size: $--font-size-base !default;
$--cascader-menu-radius: $--border-radius-base !default;
$--cascader-menu-border: solid 1px $--border-color-light !default;
$--cascader-menu-shadow: $--box-shadow-light !default;
$--cascader-node-background-hover: $--background-color-base !default;
$--cascader-node-color-disabled:$--color-text-placeholder !default;
$--cascader-color-empty:$--color-text-placeholder !default;
$--cascader-tag-background: #f0f2f5;
/* Group
-------------------------- */
$--group-option-flex: 0 0 (1/5) * 100% !default;
$--group-option-offset-bottom: 12px !default;
$--group-option-fill-hover: rgba($--color-black, 0.06) !default;
$--group-title-color: $--color-black !default;
$--group-title-font-size: $--font-size-base !default;
$--group-title-width: 66px !default;
/* Tab
-------------------------- */
$--tab-font-size: $--font-size-base !default;
$--tab-border-line: 1px solid #e4e4e4 !default;
$--tab-header-color-active: $--color-text-secondary !default;
$--tab-header-color-hover: $--color-text-regular !default;
$--tab-header-color: $--color-text-regular !default;
$--tab-header-fill-active: rgba($--color-black, 0.06) !default;
$--tab-header-fill-hover: rgba($--color-black, 0.06) !default;
$--tab-vertical-header-width: 90px !default;
$--tab-vertical-header-count-color: $--color-white !default;
$--tab-vertical-header-count-fill: $--color-text-secondary !default;
/* Button
-------------------------- */
/// fontSize||Font|1
$--button-font-size: $--font-size-base !default;
/// fontWeight||Font|1
$--button-font-weight: $--font-weight-primary !default;
/// borderRadius||Border|2
$--button-border-radius: $--border-radius-base !default;
/// padding||Spacing|3
$--button-padding-vertical: 12px !default;
/// padding||Spacing|3
$--button-padding-horizontal: 20px !default;
/// fontSize||Font|1
$--button-medium-font-size: $--font-size-base !default;
/// borderRadius||Border|2
$--button-medium-border-radius: $--border-radius-base !default;
/// padding||Spacing|3
$--button-medium-padding-vertical: 10px !default;
/// padding||Spacing|3
$--button-medium-padding-horizontal: 20px !default;
/// fontSize||Font|1
$--button-small-font-size: 12px !default;
$--button-small-border-radius: #{$--border-radius-base - 1} !default;
/// padding||Spacing|3
$--button-small-padding-vertical: 9px !default;
/// padding||Spacing|3
$--button-small-padding-horizontal: 15px !default;
/// fontSize||Font|1
$--button-mini-font-size: 12px !default;
$--button-mini-border-radius: #{$--border-radius-base - 1} !default;
/// padding||Spacing|3
$--button-mini-padding-vertical: 7px !default;
/// padding||Spacing|3
$--button-mini-padding-horizontal: 15px !default;
/// color||Color|0
$--button-default-font-color: $--color-text-regular !default;
/// color||Color|0
$--button-default-background-color: $--color-white !default;
/// color||Color|0
$--button-default-border-color: $--border-color-base !default;
/// color||Color|0
$--button-disabled-font-color: $--color-text-placeholder !default;
/// color||Color|0
$--button-disabled-background-color: $--color-white !default;
/// color||Color|0
$--button-disabled-border-color: $--border-color-lighter !default;
/// color||Color|0
$--button-primary-border-color: $--color-primary !default;
/// color||Color|0
$--button-primary-font-color: $--color-white !default;
/// color||Color|0
$--button-primary-background-color: $--color-primary !default;
/// color||Color|0
$--button-success-border-color: $--color-success !default;
/// color||Color|0
$--button-success-font-color: $--color-white !default;
/// color||Color|0
$--button-success-background-color: $--color-success !default;
/// color||Color|0
$--button-warning-border-color: $--color-warning !default;
/// color||Color|0
$--button-warning-font-color: $--color-white !default;
/// color||Color|0
$--button-warning-background-color: $--color-warning !default;
/// color||Color|0
$--button-danger-border-color: $--color-danger !default;
/// color||Color|0
$--button-danger-font-color: $--color-white !default;
/// color||Color|0
$--button-danger-background-color: $--color-danger !default;
/// color||Color|0
$--button-info-border-color: $--color-info !default;
/// color||Color|0
$--button-info-font-color: $--color-white !default;
/// color||Color|0
$--button-info-background-color: $--color-info !default;
$--button-hover-tint-percent: 20% !default;
$--button-active-shade-percent: 10% !default;
/* cascader
-------------------------- */
$--cascader-height: 200px !default;
/* Switch
-------------------------- */
/// color||Color|0
$--switch-on-color: $--color-primary !default;
/// color||Color|0
$--switch-off-color: $--border-color-base !default;
/// fontSize||Font|1
$--switch-font-size: $--font-size-base !default;
$--switch-core-border-radius: 10px !default;
// height||Other|4 TODO: width 代码写死的40px 所以下面这三个属性都没意义
$--switch-width: 40px !default;
// height||Other|4
$--switch-height: 20px !default;
// height||Other|4
$--switch-button-size: 16px !default;
/* Dialog
-------------------------- */
$--dialog-background-color: $--color-white !default;
$--dialog-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3) !default;
/// fontSize||Font|1
$--dialog-title-font-size: $--font-size-large !default;
/// fontSize||Font|1
$--dialog-content-font-size: 14px !default;
/// fontLineHeight||LineHeight|2
$--dialog-font-line-height: $--font-line-height-primary !default;
/// padding||Spacing|3
$--dialog-padding-primary: 20px !default;
/* Table
-------------------------- */
/// color||Color|0
$--table-border-color: $--border-color-lighter !default;
$--table-border: 1px solid $--table-border-color !default;
/// color||Color|0
$--table-font-color: $--color-text-regular !default;
/// color||Color|0
$--table-header-font-color: $--color-text-secondary !default;
/// color||Color|0
$--table-row-hover-background-color: $--background-color-base !default;
$--table-current-row-background-color: $--color-primary-light-9 !default;
/// color||Color|0
$--table-header-background-color: $--color-white !default;
$--table-fixed-box-shadow: 0 0 10px rgba(0, 0, 0, .12) !default;
/* Pagination
-------------------------- */
/// fontSize||Font|1
$--pagination-font-size: 13px !default;
/// color||Color|0
$--pagination-background-color: $--color-white !default;
/// color||Color|0
$--pagination-font-color: $--color-text-primary !default;
$--pagination-border-radius: 3px !default;
/// color||Color|0
$--pagination-button-color: $--color-text-primary !default;
/// height||Other|4
$--pagination-button-width: 35.5px !default;
/// height||Other|4
$--pagination-button-height: 28px !default;
/// color||Color|0
$--pagination-button-disabled-color: $--color-text-placeholder !default;
/// color||Color|0
$--pagination-button-disabled-background-color: $--color-white !default;
/// color||Color|0
$--pagination-hover-color: $--color-primary !default;
/* Popup
-------------------------- */
/// color||Color|0
$--popup-modal-background-color: $--color-black !default;
/// opacity||Other|1
$--popup-modal-opacity: 0.5 !default;
/* Popover
-------------------------- */
/// color||Color|0
$--popover-background-color: $--color-white !default;
/// fontSize||Font|1
$--popover-font-size: $--font-size-base !default;
/// color||Color|0
$--popover-border-color: $--border-color-lighter !default;
$--popover-arrow-size: 6px !default;
/// padding||Spacing|3
$--popover-padding: 12px !default;
$--popover-padding-large: 18px 20px !default;
/// fontSize||Font|1
$--popover-title-font-size: 16px !default;
/// color||Color|0
$--popover-title-font-color: $--color-text-primary !default;
/* Tooltip
-------------------------- */
/// color|1|Color|0
$--tooltip-fill: $--color-text-primary !default;
/// color|1|Color|0
$--tooltip-color: $--color-white !default;
/// fontSize||Font|1
$--tooltip-font-size: 12px !default;
/// color||Color|0
$--tooltip-border-color: $--color-text-primary !default;
$--tooltip-arrow-size: 6px !default;
/// padding||Spacing|3
$--tooltip-padding: 10px !default;
/* Tag
-------------------------- */
/// color||Color|0
$--tag-info-color: $--color-info !default;
/// color||Color|0
$--tag-primary-color: $--color-primary !default;
/// color||Color|0
$--tag-success-color: $--color-success !default;
/// color||Color|0
$--tag-warning-color: $--color-warning !default;
/// color||Color|0
$--tag-danger-color: $--color-danger !default;
/// fontSize||Font|1
$--tag-font-size: 12px !default;
$--tag-border-radius: 4px !default;
$--tag-padding: 0 10px !default;
/* Tree
-------------------------- */
/// color||Color|0
$--tree-node-hover-background-color: $--background-color-base !default;
/// color||Color|0
$--tree-font-color: $--color-text-regular !default;
/// color||Color|0
$--tree-expand-icon-color: $--color-text-placeholder !default;
/* Dropdown
-------------------------- */
$--dropdown-menu-box-shadow: $--box-shadow-light !default;
$--dropdown-menuItem-hover-fill: $--color-primary-light-9 !default;
$--dropdown-menuItem-hover-color: $--link-color !default;
/* Badge
-------------------------- */
/// color||Color|0
$--badge-background-color: $--color-danger !default;
$--badge-radius: 10px !default;
/// fontSize||Font|1
$--badge-font-size: 12px !default;
/// padding||Spacing|3
$--badge-padding: 6px !default;
/// height||Other|4
$--badge-size: 18px !default;
/* Card
--------------------------*/
/// color||Color|0
$--card-border-color: $--border-color-lighter !default;
$--card-border-radius: 4px !default;
/// padding||Spacing|3
$--card-padding: 20px !default;
/* Slider
--------------------------*/
/// color||Color|0
$--slider-main-background-color: $--color-primary !default;
/// color||Color|0
$--slider-runway-background-color: $--border-color-light !default;
$--slider-button-hover-color: mix($--color-primary, black, 97%) !default;
$--slider-stop-background-color: $--color-white !default;
$--slider-disable-color: $--color-text-placeholder !default;
$--slider-margin: 16px 0 !default;
$--slider-border-radius: 3px !default;
/// height|1|Other|4
$--slider-height: 6px !default;
/// height||Other|4
$--slider-button-size: 16px !default;
$--slider-button-wrapper-size: 36px !default;
$--slider-button-wrapper-offset: -15px !default;
/* Steps
--------------------------*/
$--steps-border-color: $--disabled-border-base !default;
$--steps-border-radius: 4px !default;
$--steps-padding: 20px !default;
/* Menu
--------------------------*/
/// fontSize||Font|1
$--menu-item-font-size: $--font-size-base !default;
/// color||Color|0
$--menu-item-font-color: $--color-text-primary !default;
/// color||Color|0
$--menu-background-color: $--color-white !default;
$--menu-item-hover-fill: $--color-primary-light-9 !default;
/* Rate
--------------------------*/
$--rate-height: 20px !default;
/// fontSize||Font|1
$--rate-font-size: $--font-size-base !default;
/// height||Other|3
$--rate-icon-size: 18px !default;
/// margin||Spacing|2
$--rate-icon-margin: 6px !default;
$--rate-icon-color: $--color-text-placeholder !default;
/* DatePicker
--------------------------*/
$--datepicker-font-color: $--color-text-regular !default;
/// color|1|Color|0
$--datepicker-off-font-color: $--color-text-placeholder !default;
/// color||Color|0
$--datepicker-header-font-color: $--color-text-regular !default;
$--datepicker-icon-color: $--color-text-primary !default;
$--datepicker-border-color: $--disabled-border-base !default;
$--datepicker-inner-border-color: #e4e4e4 !default;
/// color||Color|0
$--datepicker-inrange-background-color: $--border-color-extra-light !default;
/// color||Color|0
$--datepicker-inrange-hover-background-color: $--border-color-extra-light !default;
/// color||Color|0
$--datepicker-active-color: $--color-primary !default;
/// color||Color|0
$--datepicker-hover-font-color: $--color-primary !default;
$--datepicker-cell-hover-color: #fff !default;
/* Loading
--------------------------*/
/// height||Other|4
$--loading-spinner-size: 42px !default;
/// height||Other|4
$--loading-fullscreen-spinner-size: 50px !default;
/* Scrollbar
--------------------------*/
$--scrollbar-background-color: rgba($--color-text-secondary, .3) !default;
$--scrollbar-hover-background-color: rgba($--color-text-secondary, .5) !default;
/* Carousel
--------------------------*/
/// fontSize||Font|1
$--carousel-arrow-font-size: 12px !default;
$--carousel-arrow-size: 36px !default;
$--carousel-arrow-background: rgba(31, 45, 61, 0.11) !default;
$--carousel-arrow-hover-background: rgba(31, 45, 61, 0.23) !default;
/// width||Other|4
$--carousel-indicator-width: 30px !default;
/// height||Other|4
$--carousel-indicator-height: 2px !default;
$--carousel-indicator-padding-horizontal: 4px !default;
$--carousel-indicator-padding-vertical: 12px !default;
$--carousel-indicator-out-color: $--border-color-hover !default;
/* Collapse
--------------------------*/
/// color||Color|0
$--collapse-border-color: $--border-color-lighter !default;
/// height||Other|4
$--collapse-header-height: 48px !default;
/// color||Color|0
$--collapse-header-background-color: $--color-white !default;
/// color||Color|0
$--collapse-header-font-color: $--color-text-primary !default;
/// fontSize||Font|1
$--collapse-header-font-size: 13px !default;
/// color||Color|0
$--collapse-content-background-color: $--color-white !default;
/// fontSize||Font|1
$--collapse-content-font-size: 13px !default;
/// color||Color|0
$--collapse-content-font-color: $--color-text-primary !default;
/* Transfer
--------------------------*/
$--transfer-border-color: $--border-color-lighter !default;
$--transfer-border-radius: $--border-radius-base !default;
/// height||Other|4
$--transfer-panel-width: 200px !default;
/// height||Other|4
$--transfer-panel-header-height: 40px !default;
/// color||Color|0
$--transfer-panel-header-background-color: $--background-color-base !default;
/// height||Other|4
$--transfer-panel-footer-height: 40px !default;
/// height||Other|4
$--transfer-panel-body-height: 246px !default;
/// height||Other|4
$--transfer-item-height: 30px !default;
/// height||Other|4
$--transfer-filter-height: 32px !default;
/* Header
--------------------------*/
$--header-padding: 0 20px !default;
/* Footer
--------------------------*/
$--footer-padding: 0 20px !default;
/* Main
--------------------------*/
$--main-padding: 20px !default;
/* Timeline
--------------------------*/
$--timeline-node-size-normal: 12px !default;
$--timeline-node-size-large: 14px !default;
$--timeline-node-color: $--border-color-light !default;
/* Backtop
--------------------------*/
/// color||Color|0
$--backtop-background-color: $--color-white !default;
/// color||Color|0
$--backtop-font-color: $--color-primary !default;
/// color||Color|0
$--backtop-hover-background-color: $--border-color-extra-light !default;
/* Link
--------------------------*/
/// fontSize||Font|1
$--link-font-size: $--font-size-base !default;
/// fontWeight||Font|1
$--link-font-weight: $--font-weight-primary !default;
/// color||Color|0
$--link-default-font-color: $--color-text-regular !default;
/// color||Color|0
$--link-default-active-color: $--color-primary !default;
/// color||Color|0
$--link-disabled-font-color: $--color-text-placeholder !default;
/// color||Color|0
$--link-primary-font-color: $--color-primary !default;
/// color||Color|0
$--link-success-font-color: $--color-success !default;
/// color||Color|0
$--link-warning-font-color: $--color-warning !default;
/// color||Color|0
$--link-danger-font-color: $--color-danger !default;
/// color||Color|0
$--link-info-font-color: $--color-info !default;
/* Calendar
--------------------------*/
/// border||Other|4
$--calendar-border: $--table-border !default;
/// color||Other|4
$--calendar-selected-background-color: #F2F8FE !default;
$--calendar-cell-width: 85px !default;
/* Form
-------------------------- */
/// fontSize||Font|1
$--form-label-font-size: $--font-size-base !default;
/* Avatar
--------------------------*/
/// color||Color|0
$--avatar-font-color: #fff !default;
/// color||Color|0
$--avatar-background-color: #C0C4CC !default;
/// fontSize||Font Size|1
$--avatar-text-font-size: 14px !default;
/// fontSize||Font Size|1
$--avatar-icon-font-size: 18px !default;
/// borderRadius||Border|2
$--avatar-border-radius: $--border-radius-base !default;
/// size|1|Avatar Size|3
$--avatar-large-size: 40px !default;
/// size|1|Avatar Size|3
$--avatar-medium-size: 36px !default;
/// size|1|Avatar Size|3
$--avatar-small-size: 28px !default;
/* Break-point
--------------------------*/
$--sm: 768px !default;
$--md: 992px !default;
$--lg: 1200px !default;
$--xl: 1920px !default;
$--breakpoints: (
'xs' : (max-width: $--sm - 1),
'sm' : (min-width: $--sm),
'md' : (min-width: $--md),
'lg' : (min-width: $--lg),
'xl' : (min-width: $--xl)
);
$--breakpoints-spec: (
'xs-only' : (max-width: $--sm - 1),
'sm-and-up' : (min-width: $--sm),
'sm-only': "(min-width: #{$--sm}) and (max-width: #{$--md - 1})",
'sm-and-down': (max-width: $--md - 1),
'md-and-up' : (min-width: $--md),
'md-only': "(min-width: #{$--md}) and (max-width: #{$--lg - 1})",
'md-and-down': (max-width: $--lg - 1),
'lg-and-up' : (min-width: $--lg),
'lg-only': "(min-width: #{$--lg}) and (max-width: #{$--xl - 1})",
'lg-and-down': (max-width: $--xl - 1),
'xl-only' : (min-width: $--xl),
);

View File

View File

@@ -0,0 +1,3 @@
@import 'element-variables';
@import 'reboot';

View File

@@ -1,11 +1,11 @@
<template>
<div class="sidebar__item" :class="computedClasses">
<router-link :to="to">
<div class="sidebar__menu-item" :class="computedClasses">
<router-link class="sidebar__menu-item-anchor" :to="to">
<span class="title">{{ name }}</span>
<span v-if="count" class="count">{{ count }}</span>
</router-link>
<div v-if="hasChildren" class="sidebar__item-children">
<div v-if="hasChildren" class="sidebar__menu-item-children">
</div>
</div>
</template>

View File

@@ -3,7 +3,7 @@
<div class="sidebar__app">
<div class="sidebar__app-logo">
<span class="logo"></span>
</div>
<div class="sidebar__app-info">
@@ -12,6 +12,8 @@
</div>
</div>
<div class="divider"></div>
<div class="sidebar__menu">
<SidebarItem
v-for="(item, index) in sidebarItems" :key="index"
@@ -21,6 +23,17 @@
:children="item.children"
/>
</div>
<div class="sidebar__user">
<div class="sidebar__user-avatar">
<span class="logo"></span>
</div>
<div class="sidebar__user-info">
<h6 class="title">Ahmed Bouhuolia</h6>
<span class="info">a.bouhuolia@gmail.com</span>
</div>
</div>
</div>
</template>
@@ -44,11 +57,73 @@ export default {
};
</script>
<style>
.dashboard__sidebar{
<style lang="scss">
.sidebar{
width: 210px;
background: red;
min-width: 210px;
background: #01092d;
transition: width 0.3s;
height: 100%;
color: #fff;
min-height: 100vh;
// App info
// -------------
&__app{
margin-bottom: 20px;
display: flex;
flex-direction: column;
&-logo{
.logo {
height: 35px;
width: 35px;
border-radius: 35px;
background: red;
}
}
&-info{
.title{
margin: 0;
}
.version{
margin: 0;
}
}
}
// Menu List
// -------------
&__menu{
}
&__menu-item{
&-anchor{
color: #fff;
}
}
// User info.
// -------------
&__user{
border-top: 1px solid red;
&-info{
.title{
margin: 0;
}
.info{
font-size: 12px;
}
}
}
}
</style>

View File

@@ -1,5 +1,9 @@
<template>
<div class="topbar">
<div class="topbar__sidebar-toggle">
</div>
<div class="topbar__title">
<h3>{{ pageTitle }}</h3>
</div>
@@ -26,10 +30,12 @@ export default {
<style lang="scss">
.topbar{
padding-bottom: 12px;
width: 100%;
&__title{
h3{
margin: 0;
font-size: 30px;
}
}

4
common/utils.js Normal file
View File

@@ -0,0 +1,4 @@
export function roundTo(num, to = 2) {
return +(Math.round(num + "e+" + to) + "e-" + to);
}

41
docker/.gitignore vendored Normal file
View File

@@ -0,0 +1,41 @@
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# nyc test coverage
.nyc_output
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules
jspm_packages
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
# Project Specific
data
./docker-compose.override.yml

21
docker/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Osedea
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

385
docker/README.md Normal file
View File

@@ -0,0 +1,385 @@
![NoDock](https://raw.githubusercontent.com/Osedea/nodock/master/docs/images/logo.png)
Docker Compose for Node projects with Node, MySQL, MongoDB, NGINX, Memcached, Redis, Certbot and RabbitMQ images
![Node + Docker](https://raw.githubusercontent.com/Osedea/nodock/master/docs/images/nodock.jpg)
<a name="why-nodock"></a>
## Why NoDock?
The [docker Node.js](https://github.com/nodejs/docker-node) image is very simple, you give it an entrypoint and it runs it. This is fine for very simple/small scripts but for larger projects you'll probably want something a bit more robust.
The goal of NoDock is to provide a complete environment for your node project: Node.js service(s), databases, web servers, queues, etc. while doing the "wiring" for you.
You can use NoDock for simple projects by using one of the [examples](#Examples) or you can build upon them.
## Contents
- [Requirements](#Requirements)
- [Installation](#Installation)
- [Usage](#Usage)
- [Examples](#Examples)
- [Workspace](#Workspace)
- [HTTPS](#HTTPS)
- [Self-signed certificates](#SelfSigned)
- [Certbot](#Certbot)
- [Non-web project](#Non-Web)
- [Multiple Node containers](#Multi-Node)
- [More options](#More-Options)
- [Use Yarn](#Use-Yarn)
- [Change Node entrypoint](#Node-Entrypoint)
- [Change Node environment](#Node-Environment)
- [Change Node version](#Node-Version)
- [Change Node project location](#Node-Project-Path)
- [Change MySQL database/user/password](#MySQL-Database-User)
- [Change PostgreSQL database/user/password](#PostgreSQL-Database-User)
- [Change NGINX reverse proxy port](#NGINX-Reverse-Proxy-Port)
- [Change the timezone](#Change-the-timezone)
- [Use RabbitMQ plugins](#Use-RabbitMQ-plugins)
- [Change the RabbitMQ user/password](#Change-RabbitMQ-User)
- [Modify Redis config](#Modify-Redis-Config)
- [Contributing](#Contributing)
- [License](#License)
- [Credits](#credits)
<a name="Requirements"></a>
## Requirements
* [Docker Engine 17.06+](https://docs.docker.com/engine/installation/)
* [Docker Compose 1.8+](https://docs.docker.com/compose/install/)
<a name="Installation"></a>
## Installation
As a git submodule:
```bash
git submodule add https://github.com/Osedea/nodock.git
```
Clone into your project:
```bash
git clone https://github.com/Osedea/nodock.git
```
We recommend you [fork this repository](https://github.com/Osedea/nodock#fork-destination-box) if you intend to add project specific scripts/configurations.
<a name="Usage"></a>
## Usage
```bash
cd nodock
# Run "node" and "nginx"
docker-compose up -d node nginx
```
To overwrite the `docker-compose.yml` file you can use a `docker-compose.override.yml`
```yaml
# docker-compose.override.yml
version: '3'
services:
[...]
```
<a name="Examples"></a>
## Examples
We provide examples of configurations you might use for a specific stack. Each example has it's own README file with instructions.
* [Simple Web with Apache](https://github.com/Osedea/nodock/tree/master/_examples/apache) - Node + Apache
* [Simple Web with Nginx](https://github.com/Osedea/nodock/tree/master/_examples/nginx) - Node + NGINX
* [MySQL](https://github.com/Osedea/nodock/tree/master/_examples/mysql) - MySQL + Node + NGINX
* [PostgreSQL](https://github.com/Osedea/nodock/tree/master/_examples/postgresql) - PostgreSQL + Node + NGINX
* [Mongo](https://github.com/Osedea/nodock/tree/master/_examples/mongo) - MongoDB + Node + NGINX
* [RabbitMQ](https://github.com/Osedea/nodock/tree/master/_examples/rabbitmq) - RabbitMQ + Node + NGINX
* [Memcached](https://github.com/Osedea/nodock/tree/master/_examples/memcached) - Memcached + Node + NGINX
* [Redis](https://github.com/Osedea/nodock/tree/master/_examples/redis) - Redis + Node + NGINX
* [RethinkDB](https://github.com/Osedea/nodock/tree/master/_examples/rethinkdb) - RethinkDB + Node + NGINX
* [2 Node Apps](https://github.com/Osedea/nodock/tree/master/_examples/2-nodes) - Node + Node + NGINX
<a name="Workspace"></a>
## Workspace
The `workspace` container is where you want to be manually running commands for `NoDock`. You can use this container to initialize your project, for task-automation, for [cronjobs](#Cronjobs), etc.
<a name="HTTPS"></a>
## Using HTTPS
By default HTTPS is disabled. To enable it, you may use the following settings
```yaml
# docker-compose.override.yml
[...]
nginx:
build:
args:
- WEB_SSL=true
```
Add your certificate to `nginx/certs/cacert.pem` and the private key to `nginx/certs/privkey.pem`.
<a name="SelfSigned"></a>
#### Generate and use a self-signed cert
`SELF_SIGNED: "true"` will generate the necessary files, do note that `SELF_SIGNED: "true"` as no effect if `WEB_SSL: "false"`
```yaml
# docker-compose.override.yml
[...]
nginx:
build:
args:
- WEB_SSL=true
- SELF_SIGNED=true
```
<a name="Certbot"></a>
#### Use Certbot (Let's Encrypt) to generate the cert
`CN` must be a publicly accessible address and `EMAIL` should be the server admin contact email.
```yaml
# docker-compose.override.yml
[...]
nginx:
build:
args:
- WEB_SSL=true
certbot:
environment:
- CN=example.com
- EMAIL=fake@gmail.com
```
Don't forget to bring up the container if you plan on using certbot (`docker-compose up -d certbot`).
<a name="Non-Web"></a>
## Running a single non-web container
The default NGINX server block configuration is aimed at web projects but if you want to have a single non-web container you can do something similar to the following configuration.
```yaml
# docker-compose.override.yml
[...]
nginx:
build:
args:
-NO_DEFAULT=true
ports:
- "10000:10000"
```
Do note that using `NO_DEFAULT` makes `WEB_REVERSE_PROXY_PORT`, `WEB_SSL` and `SELF_SIGNED` have no effect.
You will then have to provide your own NGINX server block like so
```
# nginx/sites/custom-node.conf
server {
listen 10000 default_server;
location / {
proxy_pass http://node:5000;
}
}
```
<a name="Multi-Node"></a>
## Running multiple node containers
To add more node containers, simply add the following to your `docker-compose.override.yml` or environment specific docker-compose file.
```yaml
# docker-compose.override.yml
[...]
node2: # name of new container
build: # reuse the same values from the node service, cannot use extends in docker-compose 3+
context: ./node
args:
- NODE_VERSION=latest
- PROJECT_PATH=/opt/app/
- NODE_ENV=production
- YARN=false
volumes:
- ../:/opt/app
entrypoint: run-nodock "node alternate.js" # the entrypoint for the "node2" container
nginx:
ports:
- "10000:10000" # the port(s) to forward for the "node2" container
links:
- node2 # link "nginx" to "node2"
```
You'll also need to add a server block for "node2".
```
# nginx/sites/node2.conf
server {
listen 10000 default_server;
location / {
proxy_pass http://node2:8000;
}
}
```
<a name="Cronjobs"></a>
## Cronjobs
You can run cronjobs in the [Workspace](#Workspace) by storing them in the `workspace/crontab/root` file.
```
# workspace/crontab/root
* * * * * echo "Every Minute" >> /var/log/cron.log
```
<a name="More-Options"></a>
## More Options
To customize the NoDock installation, either add a `docker-compose.override.yml` in the NoDock directory or store environment specific configurations.
```bash
docker-compose -f nodock/docker-compose.yml -f docker-compose.dev.yml up -d
```
<a name="Use-Yarn"></a>
#### Use Yarn
Set the `YARN` argument to `true`.
```yaml
# docker-compose.override.yml
[...]
node:
build:
args:
- YARN=true
```
<a name="Node-Entrypoint"></a>
#### Change the node entrypoint
Use `main.js` instead of `index.js`
```yaml
# docker-compose.override.yml
[...]
node:
entrypoint: run-nodock "node main.js"
```
<a name="Node-Environment"></a>
#### Change the Node Environment
The default `NODE_ENV` value is `production`, you can change it to development by doing the following
```yaml
# docker-compose.override.yml
[...]
node:
build:
args:
- NODE_ENV=development
```
<a name="Node-Version"></a>
#### Use a specific Node version
The default node version is `latest`, this is **NOT** advisable for production
```yaml
# docker-compose.override.yml
[...]
node:
build:
args:
- NODE_VERSION=4.6.0
```
<a name="Node-Project-Path"></a>
#### Change the Node project path
You can specify a `PROJECT_PATH` to change the directory in which `npm` will perform it's `install` command and the directory in which `run-nodock` will run the entrypoint script. This is most desirable when running more than one Node project at a time since they are bound to each have their own `package.json` file.
```yaml
# docker-compose.override.yml
[...]
node:
build:
args:
PROJECT_PATH: somefolder # note that this is the same as "/opt/app/somefolder"
```
<a name="MySQL-Database-User"></a>
#### Change the MySQL database/user/password
```yaml
# docker-compose.override.yml
[...]
mysql:
build:
args:
- MYSQL_DATABASE=default_database
- MYSQL_USER=default_user
- MYSQL_PASSWORD=secret
```
<a name="PostgreSQL-Database-User"></a>
#### Change the PostgreSQL database/user/password
```yaml
# docker-compose.override.yml
[...]
postgresql:
build:
args:
- POSTGRES_DB=default_db
- POSTGRES_USER=default_user
- POSTGRES_PASSWORD=secret
```
<a name="NGINX-Reverse-Proxy-Port"></a>
#### Change the NGINX reverse proxy port
Use port `8080` instead of `8000` to bind your Node server
```yaml
# docker-compose.override.yml
[...]
nginx:
build:
args:
- WEB_REVERSE_PROXY_PORT=8080
```
<a name="Change-the-timezone"></a>
#### Change the timezone
To change the timezone for the `workspace` container, modify the `TZ` build argument in the Docker Compose file to one in the [TZ database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones).
For example, if I want the timezone to be `New York`:
```yaml
# docker-compose.override.yml
[...]
workspace:
build:
context: ./workspace
args:
- TZ="America/New_York"
```
<a name="Use-RabbitMQ-plugins"></a>
#### Use RabbitMQ plugins
At the moment, NoDock supports 2 plugins: [Management](https://www.rabbitmq.com/management.html) and [Federation](https://www.rabbitmq.com/federation.html).
To activate them, change their values to `true` in your docker-compose file:
```yaml
# docker-compose.override.yml
[...]
rabbitmq:
build:
args:
- MANAGEMENT=true
- FEDERATION=true
```
<a name="Change-RabbitMQ-User"></a>
#### Change the RabbitMQ user/password
```yaml
# docker-compose.override.yml
[...]
rabbitmq:
build:
args:
- RABBITMQ_DEFAULT_USER=custom_user
- RABBITMQ_DEFAULT_PASS=custom_pass
```
<a name="Modify-Redis-Config"></a>
#### Modify the Redis config
You can edit `redis/redis.conf` to modify the redis config.
<a name="Contributing"></a>
## Contributing
Do not hesitate to contribute to NoDock by creating an issue, fixing a bug or bringing a new idea to the table.
To fix a bug or introduce a new feature, please create a PR, we will merge it in to the `master` branch after review.
We thank you in advance for contributing.
<a name="License"></a>
## License
[MIT License](https://github.com/laradock/laradock/blob/master/LICENSE) (MIT)
<a name="credits"></a>
## Credits
NoDock uses Open Source components. You can find the source code of their open source projects along with license information below. We acknowledge and are grateful to these developers for their contributions to open source.
Project: LaraDock https://github.com/LaraDock/laradock <br>
Copyright (c) 2016 Mahmoud Zalt (mahmoud@zalt.me) <br>
License (MIT) https://github.com/LaraDock/laradock/blob/master/LICENSE <br>
Project: baseimage-docker https://github.com/phusion/baseimage-docker <br>
Copyright (c) 2013-2015 Phusion Holding B.V. <br>
License (MIT) https://github.com/phusion/baseimage-docker/blob/master/LICENSE.txt <br>

30
docker/apache/Dockerfile Normal file
View File

@@ -0,0 +1,30 @@
FROM httpd:2.4
# openssl not installed in image
RUN apt-get update && apt-get install openssl
RUN mkdir /usr/local/apache2/templates \
&& mkdir /usr/local/apache2/sites-available \
&& rm /usr/local/apache2/conf/httpd.conf \
&& rm /usr/local/apache2/conf/extra/*.conf
COPY httpd.conf /usr/local/apache2/conf/httpd.conf
COPY scripts /root/scripts/
COPY certs /etc/ssl/
COPY sites /usr/local/apache2/templates
ARG WEB_REVERSE_PROXY_PORT=8000
ARG WEB_SSL=false
ARG SELF_SIGNED=false
ARG NO_DEFAULT=false
ENV WEB_REVERSE_PROXY_PORT=$WEB_REVERSE_PROXY_PORT
ENV WEB_SSL=$WEB_SSL
ENV SELF_SIGNED=$SELF_SIGNED
ENV NO_DEFAULT=$NO_DEFAULT
RUN /bin/bash /root/scripts/build-apache.sh
CMD ["apachectl", "-D", "FOREGROUND"]

View File

100
docker/apache/httpd.conf Normal file
View File

@@ -0,0 +1,100 @@
ServerRoot "/usr/local/apache2"
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_host_module modules/mod_authz_host.so
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
LoadModule authz_user_module modules/mod_authz_user.so
LoadModule authz_core_module modules/mod_authz_core.so
LoadModule access_compat_module modules/mod_access_compat.so
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule reqtimeout_module modules/mod_reqtimeout.so
LoadModule filter_module modules/mod_filter.so
LoadModule mime_module modules/mod_mime.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule env_module modules/mod_env.so
LoadModule headers_module modules/mod_headers.so
LoadModule setenvif_module modules/mod_setenvif.so
LoadModule version_module modules/mod_version.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule ssl_module modules/mod_ssl.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule status_module modules/mod_status.so
LoadModule autoindex_module modules/mod_autoindex.so
LoadModule dir_module modules/mod_dir.so
LoadModule alias_module modules/mod_alias.so
<IfModule unixd_module>
User www-data
Group www-data
</IfModule>
ServerAdmin you@example.com
<Directory />
AllowOverride none
Require all denied
</Directory>
DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
<IfModule dir_module>
DirectoryIndex index.html
</IfModule>
<Files ".ht*">
Require all denied
</Files>
ErrorLog /proc/self/fd/2
LogLevel warn
<IfModule log_config_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
<IfModule logio_module>
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
</IfModule>
CustomLog /proc/self/fd/1 common
</IfModule>
<IfModule alias_module>
ScriptAlias /cgi-bin/ "/usr/local/apache2/cgi-bin/"
</IfModule>
<Directory "/usr/local/apache2/cgi-bin">
AllowOverride None
Options None
Require all granted
</Directory>
<IfModule headers_module>
RequestHeader unset Proxy early
</IfModule>
<IfModule mime_module>
TypesConfig conf/mime.types
AddType application/x-compress .Z
AddType application/x-gzip .gz .tgz
</IfModule>
Include sites-available/*.conf
<IfModule proxy_html_module>
Include conf/extra/proxy-html.conf
</IfModule>
<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
</IfModule>

View File

@@ -0,0 +1,20 @@
#!/bin/bash
for conf in /usr/local/apache2/templates/*.conf; do
mv $conf "/usr/local/apache2/sites-available/"$(basename $conf) > /dev/null
done
for template in /usr/local/apache2/templates/*.template; do
mv $template "/usr/local/apache2/sites-available/"$(basename $template)".conf" > /dev/null
done
if [[ "$NO_DEFAULT" = true ]]; then
rm /usr/local/apache2/sites-available/node.template.conf
rm /usr/local/apache2/sites-available/node-https.template.conf
else
if [[ "$WEB_SSL" = false ]]; then
rm /usr/local/apache2/sites-available/node-https.template.conf
fi
fi
. /root/scripts/run-openssl.sh

View File

@@ -0,0 +1,31 @@
#!/bin/bash
if [[ "$WEB_SSL" = true && "$NO_DEFAULT" = false ]]; then
if [[ "$SELF_SIGNED" = true ]]; then
echo "---------------------------------------------------------"
echo "APACHE: Generating certificates"
echo "---------------------------------------------------------"
openssl req \
-new \
-newkey rsa:4096 \
-days 1095 \
-nodes \
-x509 \
-subj "/C=FK/ST=Fake/L=Fake/O=Fake/CN=0.0.0.0" \
-keyout /etc/ssl/privkey1.pem \
-out /etc/ssl/cert1.pem
chown www-data:www-data /etc/ssl/cert1.pem
chown www-data:www-data /etc/ssl/privkey1.pem
else
echo "---------------------------------------------------------"
echo "APACHE: Using certificates in 'nodock/apache/certs/'"
echo "---------------------------------------------------------"
if [ -e /var/certs/cert1.pem ]; then
cp /var/certs/cert1.pem /etc/ssl/cert1.pem
fi
if [ -e /var/certs/privkey1.pem ]; then
cp /var/certs/privkey1.pem /etc/ssl/privkey1.pem
fi
fi
fi

View File

@@ -0,0 +1,17 @@
# environment variables
# WEB_REVERSE_PROXY_PORT ${WEB_REVERSE_PROXY_PORT}
Listen 443
<VirtualHost *:443>
SSLEngine on
SSLCertificateFile /etc/ssl/cert1.pem
SSLCertificateKeyFile /etc/ssl/privkey1.pem
ProxyPass / http://node:${WEB_REVERSE_PROXY_PORT}
ErrorLog logs/https-error.log
CustomLog logs/https-access.log common
</VirtualHost>

View File

@@ -0,0 +1,14 @@
# environment variables
# WEB_REVERSE_PROXY_PORT ${WEB_REVERSE_PROXY_PORT}
Listen 80
<VirtualHost *:80>
ProxyPass / http://node:${WEB_REVERSE_PROXY_PORT}
# ProxyPassReverse / http://node:${WEB_REVERSE_PROXY_PORT}
ErrorLog logs/http-error.log
CustomLog logs/http-access.log common
</VirtualHost>

View File

@@ -0,0 +1,8 @@
FROM phusion/baseimage:0.9.19
COPY scripts /root/scripts/
RUN apt-get update
RUN apt-get install -y letsencrypt
ENTRYPOINT bash -c "bash /root/scripts/run-certbot.sh && sleep infinity"

1
docker/certbot/certs/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.pem

View File

View File

View File

@@ -0,0 +1,6 @@
#!/bin/bash
letsencrypt certonly --webroot -w /var/www/letsencrypt -d "$CN" --agree-tos --email "$EMAIL" --non-interactive --text
cp /etc/letsencrypt/archive/"$CN"/cert1.pem /var/certs/cert1.pem
cp /etc/letsencrypt/archive/"$CN"/privkey1.pem /var/certs/privkey1.pem

10
docker/composehub.yml Normal file
View File

@@ -0,0 +1,10 @@
---
name: NoDock
blurb: NoDock is the Docker solution for Node. Open-source full-stack environment for your Node projects
description: |
Framework Agnostic: MEAN, Meteor, Sails, etc. Run the stack you choose, the way you want. Totally unopinionated, fully customizable. Better, Faster & Stronger: Docker + Docker Compose ensures your environment is fast and stable for development, testing and production. Focus on Code: Plug-and-play configurations allow you to get started in minutes. Reduce the learning curve for new developers.
email: philippe.trepanier@osedea.com
repo_url: https://github.com/Osedea/nodock
tags: node,docker,nginx,apache,mysql,certbot,yarn,mongodb,memcached,rabbitmq
private: false
cmd:

54
docker/docker-compose.yml Normal file
View File

@@ -0,0 +1,54 @@
version: '3.3'
services:
node:
build:
context: ./node
args:
- NODE_VERSION=latest
- PROJECT_PATH=/opt/app/
- NODE_ENV=production
- YARN=false
volumes:
- ../server:/opt/app
entrypoint: run-nodock "knex migrate:latest && node build/bundle.js"
ports:
- "3000:3000"
expose:
- "3000"
tty: true
mysql:
build:
context: ./mysql
volumes:
- ../data/mysql/:/var/lib/mysql
expose:
- "3306"
nginx:
build:
context: ./nginx
args:
- WEB_REVERSE_PROXY_PORT=3000
- WEB_SSL=false
- SELF_SIGNED=false
- NO_DEFAULT=false
volumes:
- ../data/logs/nginx/:/var/log/nginx
- ./certbot/certs/:/var/certs
- ../client/dist/:/var/www/moosher
ports:
- "80:80"
- "443:443"
tty: true
certbot:
build:
context: ./certbot
links:
- nginx
volumes:
- ./certbot/letsencrypt/:/var/www/letsencrypt
- ./certbot/certs/:/var/certs

1
docker/docs/CNAME Normal file
View File

@@ -0,0 +1 @@
nodock.io

190
docker/docs/css/styles.css Normal file
View File

@@ -0,0 +1,190 @@
html, body {
color: #666666;
font-family: 'Roboto', sans-serif;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-size: 15px;
}
code, pre {
text-align: left;
}
h3 {
font-size: 20px;
color: #4A4A4A;
position: relative;
}
.uppercase {
text-transform: uppercase;
}
h3 img {
position: absolute;
display: none;
}
.lightning-icon {
left: -34px;
}
.box-icon {
left: -43px;
}
h5 {
font-size: 14px;
color: #9B9B9B;
margin: 10px 0 18px 0;
}
.gist {
font-size: 13px;
line-height: 18px;
margin-bottom: 30px;
width: 100%;
}
.gist pre {
font-family: Menlo, Monaco, 'Bitstream Vera Sans Mono', 'Courier New', monospace !important;
}
.gist .gist-file {
border-bottom: none !important;
background-color: #FBFBFB !important;
}
.gist-meta {
display: none;
}
.top-section {
background-color: #FBFBFB;
border-bottom: 1px solid #E0E0E0;
}
.middle-section {
padding: 60px 0 0 0;
}
.middle-section .container {
margin-bottom: 60px;
}
.logo {
margin: 80px auto;
display: block;
}
.intro {
font-weight: 300;
margin: 120px auto 60px auto;
font-size: 21px;
text-align: center;
}
.intro span {
font-size: 16px;
}
.tech-logos {
margin: 30px auto 0 auto;
width: 100%;
}
footer {
height: 120px;
background-color: #000;
padding: 30px 0;
}
.osedea-logo {
margin: 0 auto;
display: block;
}
.cta {
height: 40px;
border: 1px solid #EE4028;
border-radius: 8px;
color: #EE4028;
font-size: 15px;
width: 300px;
margin: 90px auto;
display: block;
line-height: 40px;
text-align: center;
background-color: #FFF;
transition: background-color 0.3s ease;
transition: color 0.3s ease;
}
.cta:hover {
background-color: #EE4028;
color: #FFF;
text-decoration: none;
transition: background-color 0.3s ease;
transition: color 0.3s ease;
}
.middle-container:last-child {
margin-bottom: 0;
}
copyright {
color: #FFF;
font-size: 12px;
position: initial;
text-align: center;
display: block;
padding-top: 15px;
}
copyright a {
color: #FFF;
text-decoration: underline;
}
copyright a:hover {
color: #FFF;
}
@media (min-width: 768px) {
.intro {
font-size: 30px;
margin: 140px auto 0 auto;
}
.intro span {
font-size: 22px;
}
footer {
height: 80px;
}
.osedea-logo {
margin: 0;
display: inline;
}
copyright {
position: relative;
top: 3px;
text-align: right;
}
}
@media (min-width: 860px) {
h3 img {
display: block;
}
}
@media (min-width: 820px) {
.container{
max-width: 820px !important;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 724 B

BIN
docker/docs/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

113
docker/docs/index.html Normal file
View File

@@ -0,0 +1,113 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>NoDock</title>
<meta name="description" content="NoDock is the Docker solution for Node - Open-source full-stack environment for your Node projects.">
<meta name="keywords" content="node,docker,nodock,compose,docker-compose,web,development,container,certbot,nginx,mysql,memcached,mongodb,rabbitmq,opensource">
<meta property="og:url" content="http://nodock.io/">
<meta property="og:type" content="website">
<meta property="og:title" content="NoDock | Full-stack Docker environment for Node projects">
<meta property="og:description" content="NoDock is the Docker solution for Node - Open-source full-stack environment for your Node projects.">
<meta property="og:image" content="http://nodock.io/images/logo.png">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="NoDock | Full-stack Docker environment for Node projects">
<meta name="twitter:description" content="NoDock is the Docker solution for Node - Open-source full-stack environment for your Node projects.">
<meta name="twitter:image" content="http://nodock.io/images/logo.png">
<meta name="twitter:site" content="@osedea">
<meta name="twitter:creator" content="@osedea">
<meta name="viewport" content="initial-scale=1.0,user-scalable=no,maximum-scale=1,width=device-width">
<link rel="icon" href="images/favicon.png" type="image/png">
<link href="https://fonts.googleapis.com/css?family=Roboto:100,300,400" rel="stylesheet" type="text/css">
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<link rel="stylesheet" href="css/styles.css" type="text/css">
</head>
<body>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-87277646-1', 'auto');
ga('send', 'pageview');
</script>
<a href="https://github.com/Osedea/nodock"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://camo.githubusercontent.com/365986a132ccd6a44c23a9169022c0b5c890c387/68747470733a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67" alt="Fork me on GitHub" data-canonical-src="https://s3.amazonaws.com/github/ribbons/forkme_right_red_aa0000.png"></a>
<div class="top-section">
<div class="container">
<div class="row">
<div class="col-xs-12">
<a href="https://github.com/Osedea/nodock">
<img class="logo" src="images/logo.png" alt="NoDock Logo" title="NoDock Logo" />
</a>
<p class="intro">NoDock is the Docker solution for Node<br><span>Open-source full-stack environment for your Node projects</span></p>
</div>
<div class="col-xs-12">
<a class="cta" href="https://github.com/Osedea/nodock">Contribute on GitHub</a>
</div>
</div>
</div>
</div>
<div class="middle-section">
<div class="middle-container container">
<div class="row">
<div class="col-xs-12 col-sm-4">
<h3>Framework Agnostic</h3>
<p>
MEAN, Meteor, Sails, etc. Run the stack you choose, the way you want. Totally unopinionated, fully customizable.
</p>
</div>
<div class="col-xs-12 col-sm-4">
<h3>Better, Faster &amp; Stronger</h3>
<p>
Docker + Docker Compose v3 ensures your environment is fast and stable for development, testing and production.
</p>
</div>
<div class="col-xs-12 col-sm-4">
<h3>Focus on Code</h3>
<p>
Plug-and-play configurations allow you to get started in minutes. Reduce the learning curve for new developers.
</p>
</div>
</div>
</div>
<div class="middle-container container">
<div class="row">
<div class="col-xs-12">
<h3><img class="box-icon" src="images/box-icon.png" alt="Box Icon" title="Box Icon" />Easy to set up</h3>
<h5>It takes 2 minutes to get an app up:</h5>
<script src="https://gist.github.com/philtrep/716129ed0969ce2641dd56a63c2d7071.js"></script>
<h5>Et voilà! You can see the example up on localhost.</h5>
</div>
</div>
</div>
<div class="middle-container container">
<div class="row">
<div class="col-xs-12">
<h3><img class="lightning-icon" src="images/lightning-icon.png" alt="Lightning Icon" title="Lightning Icon" />Currently supports</h3>
<img class="tech-logos" src="images/tech-logos.png" alt="Tech Logo" title="Tech Logo" />
<h5 class="text-right">... with more coming!</h5>
</div>
<div class="col-xs-12">
<a class="cta" href="https://github.com/Osedea/nodock">Contribute on GitHub</a>
</div>
</div>
</div>
</div>
<footer>
<div class="container">
<div class="row">
<div class="col-xs-12 col-sm-3">
<a href="https://osedea.com/">
<img class="osedea-logo" src="images/osedea-logo.png" alt="Osedea Logo" title="Osedea Logo" />
</a>
</div>
<div class="col-xs-12 col-sm-9">
<copyright>
<a href="https://osedea.com/">Osedea</a> is proud to be supporting NoDock, <a href="https://github.com/Osedea">we <3 open source</a>
</copyright>
</div>
</div>
</div>
</footer>
</body>
</html>

View File

@@ -0,0 +1,3 @@
FROM memcached:1.4
CMD memcached

9
docker/mongo/Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
FROM mongo:3.3
# Add mongo user
RUN groupadd -r mongo &&\
useradd -r -g mongo mongo
USER mongo
CMD mongod --dbpath=/var/lib/mongodb

18
docker/mysql/Dockerfile Normal file
View File

@@ -0,0 +1,18 @@
FROM mysql:5.7
ADD my.cnf /etc/mysql/conf.d/my.cnf
RUN chown -R mysql:root /var/lib/mysql/
ARG MYSQL_DATABASE=moosher_database
ARG MYSQL_USER=moosher_user
ARG MYSQL_PASSWORD=root
ARG MYSQL_ROOT_PASSWORD=root
ENV MYSQL_DATABASE=$MYSQL_DATABASE
ENV MYSQL_USER=$MYSQL_USER
ENV MYSQL_PASSWORD=$MYSQL_PASSWORD
ENV MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD
CMD ["mysqld"]
EXPOSE 3306

2
docker/mysql/my.cnf Normal file
View File

@@ -0,0 +1,2 @@
[mysqld]
bind-address = 0.0.0.0

23
docker/nginx/Dockerfile Normal file
View File

@@ -0,0 +1,23 @@
FROM nginx:1.11
RUN mkdir /etc/nginx/sites-available && rm /etc/nginx/conf.d/default.conf
ADD nginx.conf /etc/nginx/
COPY scripts /root/scripts/
COPY certs /etc/ssl/
COPY sites /etc/nginx/templates
ARG WEB_REVERSE_PROXY_PORT=3000
ARG WEB_SSL=false
ARG SELF_SIGNED=false
ARG NO_DEFAULT=false
ENV WEB_REVERSE_PROXY_PORT=$WEB_REVERSE_PROXY_PORT
ENV WEB_SSL=$WEB_SSL
ENV SELF_SIGNED=$SELF_SIGNED
ENV NO_DEFAULT=$NO_DEFAULT
RUN /bin/bash /root/scripts/build-nginx.sh
CMD nginx

View File

33
docker/nginx/nginx.conf Normal file
View File

@@ -0,0 +1,33 @@
user www-data;
worker_processes auto;
pid /run/nginx.pid;
daemon off;
events {
worker_connections 2048;
use epoll;
}
http {
server_tokens off;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 15;
types_hash_max_size 2048;
client_max_body_size 20M;
open_file_cache max=100;
gzip on;
gzip_disable "msie6";
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
include /etc/nginx/mime.types;
default_type application/octet-stream;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-available/*;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
}

View File

@@ -0,0 +1,20 @@
#!/bin/bash
for conf in /etc/nginx/templates/*.conf; do
mv $conf "/etc/nginx/sites-available/"$(basename $conf) > /dev/null
done
for template in /etc/nginx/templates/*.template; do
envsubst < $template > "/etc/nginx/sites-available/"$(basename $template)".conf"
done
if [[ "$NO_DEFAULT" = true ]]; then
rm /etc/nginx/sites-available/node.template.conf
rm /etc/nginx/sites-available/node-https.template.conf
else
if [[ "$WEB_SSL" = false ]]; then
rm /etc/nginx/sites-available/node-https.template.conf
fi
fi
. /root/scripts/run-openssl.sh

View File

@@ -0,0 +1,31 @@
#!/bin/bash
if [[ "$WEB_SSL" = true && "$NO_DEFAULT" = false ]]; then
if [[ "$SELF_SIGNED" = true ]]; then
echo "---------------------------------------------------------"
echo "APACHE: Generating certificates"
echo "---------------------------------------------------------"
openssl req \
-new \
-newkey rsa:4096 \
-days 1095 \
-nodes \
-x509 \
-subj "/C=FK/ST=Fake/L=Fake/O=Fake/CN=0.0.0.0" \
-keyout /etc/ssl/privkey1.pem \
-out /etc/ssl/cert1.pem
chown www-data:www-data /etc/ssl/cert1.pem
chown www-data:www-data /etc/ssl/privkey1.pem
else
echo "---------------------------------------------------------"
echo "APACHE: Using certificates in 'nodock/apache/certs/'"
echo "---------------------------------------------------------"
if [ -e /var/certs/cert1.pem ]; then
cp /var/certs/cert1.pem /etc/ssl/cert1.pem
fi
if [ -e /var/certs/privkey1.pem ]; then
cp /var/certs/privkey1.pem /etc/ssl/privkey1.pem
fi
fi
fi

View File

@@ -0,0 +1,13 @@
# environment variables
# WEB_REVERSE_PROXY_PORT ${WEB_REVERSE_PROXY_PORT}
server {
listen 443 default_server http2;
ssl on;
ssl_certificate /etc/ssl/cert1.pem;
ssl_certificate_key /etc/ssl/privkey1.pem;
location / {
proxy_pass http://node:${WEB_REVERSE_PROXY_PORT};
}
}

View File

@@ -0,0 +1,32 @@
# environment variables
# WEB_REVERSE_PROXY_PORT ${WEB_REVERSE_PROXY_PORT}
server {
listen 80 default_server;
listen [::]:80 default_server;
# SSL configuration
#
# listen 443 ssl default_server;
# listen [::]:443 ssl default_server;
#
# Note: You should disable gzip for SSL traffic.
# See: https://bugs.debian.org/773332
#
# Read up on ssl_ciphers to ensure a secure configuration.
# See: https://bugs.debian.org/765782
#
# Self signed certs generated by the ssl-cert package
# Don't use them in a production server!
#
# include snippets/snakeoil.conf;
root /var/www/moosher;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
location /api {
proxy_pass http://node:${WEB_REVERSE_PROXY_PORT};
}
}

38
docker/node/Dockerfile Normal file
View File

@@ -0,0 +1,38 @@
FROM phusion/baseimage:0.9.19
RUN apt-get update &&\
apt-get install -y npm &&\
npm install -g n
ARG NODE_ENV=production
ARG NODE_VERSION=latest
ARG PROJECT_PATH=/opt/app/
ARG YARN=false
ENV YARN=$YARN
ENV PROJECT_PATH=$PROJECT_PATH
ENV NODE_ENV=$NODE_ENV
# Add
RUN groupadd -r www-app &&\
useradd -r -g www-app www-app
RUN mkdir -p /home/www-app &&\
chmod 777 /home/www-app -R
# Install the specified NODE_VERSION or grab latest
RUN n "$NODE_VERSION"
# Install Yarn
RUN if [ ${YARN} = true ]; then \
npm install -g yarn \
;fi
COPY scripts/run-nodock.sh /usr/bin/run-nodock
RUN chmod 700 /usr/bin/run-nodock
RUN npm install webpack -g
RUN npm install knex -g
WORKDIR /opt/app

View File

@@ -0,0 +1,21 @@
#!/bin/bash
SCRIPT="$1"
if [[ ${PROJECT_PATH:0:1} = "/" ]]; then
export PROJECT_PATH=$PROJECT_PATH
else
export PROJECT_PATH="/opt/app/"$PROJECT_PATH
fi
cd $PROJECT_PATH
if [[ $YARN = true ]]; then
su -c "cd $PROJECT_PATH; yarn" -s /bin/bash www-app
else
su -c "cd $PROJECT_PATH; npm i --force" -s /bin/bash www-app
fi
chown -R www-app:www-app /opt/app
su -c "cd $PROJECT_PATH; $SCRIPT" -s /bin/bash www-app

View File

@@ -0,0 +1,11 @@
FROM postgres:10.2
ARG POSTGRES_USER=default_user
ARG POSTGRES_PASSWORD=secret
ARG POSTGRES_DB=default_db
ENV POSTGRES_USER=$POSTGRES_USER
ENV POSTGRES_PASSWORD=$POSTGRES_PASSWORD
ENV POSTGRES_DB=$POSTGRES_DB
EXPOSE 5432

View File

@@ -0,0 +1,15 @@
FROM rabbitmq
COPY scripts /root/scripts/
ARG MANAGEMENT=false
ARG FEDERATION=false
ARG RABBITMQ_DEFAULT_USER=guest
ARG RABBITMQ_DEFAULT_PASS=guest
ENV MANAGEMENT=$MANAGEMENT
ENV FEDERATION=$FEDERATION
ENV RABBITMQ_DEFAULT_USER=$RABBITMQ_DEFAULT_USER
ENV RABBITMQ_DEFAULT_PASS=$RABBITMQ_DEFAULT_PASS
RUN /bin/bash /root/scripts/build-rabbitmq.sh

View File

@@ -0,0 +1,13 @@
#!/bin/bash
if [[ "$MANAGEMENT" = true ]]; then
rabbitmq-plugins enable --offline rabbitmq_management
fi
if [[ "$FEDERATION" = true ]]; then
rabbitmq-plugins enable --offline rabbitmq_federation
if [[ "$MANAGEMENT" = true ]]; then
rabbitmq-plugins enable --offline rabbitmq_federation_management
fi
fi

5
docker/redis/Dockerfile Normal file
View File

@@ -0,0 +1,5 @@
FROM redis:4.0
COPY redis.conf /usr/local/etc/redis/redis.conf
CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ]

48
docker/redis/redis.conf Normal file
View File

@@ -0,0 +1,48 @@
daemonize no
pidfile /var/run/redis.pid
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 0
loglevel notice
logfile ""
databases 16
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
slave-serve-stale-data yes
slave-read-only yes
repl-diskless-sync no
repl-diskless-sync-delay 5
repl-disable-tcp-nodelay no
slave-priority 100
appendonly no
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
lua-time-limit 5000
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
hll-sparse-max-bytes 3000
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes

View File

@@ -0,0 +1,3 @@
FROM rethinkdb
CMD ["rethinkdb", "--bind", "all"]

View File

@@ -0,0 +1,39 @@
FROM phusion/baseimage:0.9.19
RUN apt-get update && \
apt-get install -y npm \
mysql-client \
sqlite3 \
iputils-ping && \
npm install -g n
##
## Timezone
##
ARG TZ=UTC
ENV TZ ${TZ}
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
##
## Node
##
ARG NODE_VERSION=latest
# Install the specified NODE_VERSION or grab latest
RUN n "$NODE_VERSION"
# Install YARN
RUN npm i -g yarn
##
## Cron
##
COPY ./crontab /var/spool/cron/crontabs
WORKDIR /opt/app
ENTRYPOINT sleep infinity

View File

@@ -0,0 +1,2 @@
# This is the root crontab file
# * * * * * echo "Every Minute" >> /var/log/cron.log

6
server/.babelrc Normal file
View File

@@ -0,0 +1,6 @@
{
"presets": ["@babel/preset-env"],
"retainLines": true,
"plugins": ["@babel/plugin-transform-runtime"]
}

14
server/.env Normal file
View File

@@ -0,0 +1,14 @@
MAIL_HOST=
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_PORT=
MAIL_SECURE=false
MAIL_FROM_ADDRESS=
MAIL_FROM_NAME=
DB_CLIENT=mysql
DB_HOST=127.0.0.1
DB_USER=root
DB_PASSWORD=root
DB_NAME=moosher

14
server/.env.test Normal file
View File

@@ -0,0 +1,14 @@
MAIL_HOST=
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_PORT=
MAIL_SECURE=false
MAIL_FROM_ADDRESS=
MAIL_FROM_NAME=
DB_CLIENT=mysql
DB_HOST=127.0.0.1
DB_USER=root
DB_PASSWORD=root
DB_NAME=moosher

41
server/.eslintrc.js Normal file
View File

@@ -0,0 +1,41 @@
module.exports = {
env: {
browser: false,
es6: true,
node: true,
mocha: true,
},
extends: [
'airbnb-base',
],
plugins: [
'import',
],
globals: {
Atomics: 'readonly',
SharedArrayBuffer: 'readonly',
},
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
},
rules: {
"no-unused-vars": [
"error",
{
"varsIgnorePattern": "describe|afterEach|beforeEach"
}
],
"import/no-extraneous-dependencies": [
"error", {"devDependencies": true}
],
},
settings: {
'import/resolver': {
node: {},
webpack: {
config: 'webpack.config.js'
}
},
},
};

6
server/config/index.js Normal file
View File

@@ -0,0 +1,6 @@
import path from 'path';
import dotenv from 'dotenv';
dotenv.config({
path: path.resolve(process.cwd(), '.env.test'),
});

351
server/dist/bundle.js vendored Normal file

File diff suppressed because one or more lines are too long

1
server/dist/bundle.js.map vendored Normal file

File diff suppressed because one or more lines are too long

51
server/knexfile.js Normal file
View File

@@ -0,0 +1,51 @@
const MIGRATIONS_DIR = `./${__dirname}/src/database/migrations`;
const SEEDS_DIR = `./${__dirname}/src/database/seeds`;
module.exports = {
test: {
client: process.env.DB_CLIENT,
migrations: {
directory: MIGRATIONS_DIR,
},
connection: {
host: '172.17.0.2',
user: 'root',
password: 'root',
database: 'moosher',
charset: 'utf8',
},
},
development: {
client: process.env.DB_CLIENT,
connection: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
charset: 'utf8',
},
migrations: {
directory: MIGRATIONS_DIR,
},
seeds: {
directory: SEEDS_DIR,
},
},
production: {
client: process.env.DB_CLIENT,
connection: {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
charset: 'utf8',
},
migrations: {
directory: MIGRATIONS_DIR,
},
seeds: {
directory: SEEDS_DIR,
},
},
};

7108
server/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,19 +4,61 @@
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"build": "webpack",
"start": "npm-run-all --parallel watch:server watch:build",
"watch:build": "webpack --watch",
"watch:server": "nodemon --inspect=\"9229\" \"./dist/bundle.js\" --watch \"./dist\" ",
"test": "cross-env NODE_ENV=test mocha-webpack --webpack-config webpack.config.js \"tests/**/*.test.js\"",
"test:watch": "cross-env NODE_ENV=test mocha-webpack --watch --webpack-config webpack.config.js --timeout=30000 tests/**/*.test.js"
},
"author": "",
"author": "Ahmed Bouhuolia, <a.bouhuolia@gmail.com>",
"license": "ISC",
"dependencies": {
"@hapi/boom": "^7.4.3",
"bcryptjs": "^2.4.3",
"bookshelf": "^0.15.1",
"bookshelf-json-columns": "^2.1.1",
"bookshelf-modelbase": "^2.10.4",
"bookshelf-paranoia": "^0.13.1",
"dotenv": "^8.1.0",
"errorhandler": "^1.5.1",
"express": "^4.17.1",
"express-boom": "^3.0.0",
"express-oauth-server": "^2.0.0",
"express-validator": "^6.1.1",
"jsonwebtoken": "^8.5.1",
"knex": "^0.19.2",
"mysql2": "^1.6.5"
"moment": "^2.24.0",
"mustache": "^3.0.3",
"mysql2": "^1.6.5",
"nodemailer": "^6.3.0",
"nodemon": "^1.19.1"
},
"devDependencies": {
"@babel/core": "^7.5.5",
"@babel/plugin-transform-runtime": "^7.5.5",
"@babel/polyfill": "^7.4.4",
"@babel/preset-env": "^7.5.5",
"@babel/runtime": "^7.5.5",
"babel-loader": "^8.0.6",
"chai": "^4.2.0",
"chai-http": "^4.3.0",
"chai-things": "^0.2.0",
"cross-env": "^5.2.0",
"eslint": "^6.2.1",
"eslint-config-airbnb-base": "^14.0.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-import-resolver-webpack": "^0.11.1",
"eslint-loader": "^2.2.1",
"eslint-plugin-import": "^2.18.2",
"faker": "^4.1.0",
"knex-factory": "0.0.6",
"mocha": "^5.2.0",
"mocha-webpack": "^2.0.0-beta.0",
"npm-run-all": "^4.1.5",
"nyc": "^14.1.1",
"webpack": "^4.0.0",
"webpack-cli": "^3.3.7",
"webpack-node-externals": "^1.7.2"
}
}

View File

@@ -0,0 +1,31 @@
MYSQL_USER="moosher"
MYSQL_DATABASE="moosher"
MYSQL_CONTAINER_NAME="moosher_test"
MYSQL_ROOT_PASSWORD="root"
MYSQL_PASSWORD="root"
echo "Start the testing MySql database..."
docker \
run \
--detach \
--env MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD} \
--env MYSQL_USER=${MYSQL_USER} \
--env MYSQL_PASSWORD=${MYSQL_PASSWORD} \
--env MYSQL_DATABASE=${MYSQL_DATABASE} \
--name ${MYSQL_CONTAINER_NAME} \
--publish 3306:3306 \
--tmpfs /var/lib/mysql:rw \
mysql:5.7;
echo "Sleeping for 10 seconds to allow time for the DB to be provisioned:"
for i in `seq 1 10`;
do
echo "."
sleep 1
done
echo "Database '${MYSQL_DATABASE}' running."
echo " Username: ${MYSQL_USER}"
echo " Password: ${MYSQL_PASSWORD}"

View File

@@ -1,21 +0,0 @@
import path from 'path';
import dotenv from 'dotenv';
import errorHandler from 'errorhandler';
import app from './http/app';
dotenv.config({
path: path.join(__dirname, '.env')
});
app.use(errorHandler);
const server = app.listen(app.get('port'), () => {
console.log(
" App is running at http://localhost:%d in %s mode",
app.get("port"),
app.get("env")
);
console.log(" Press CTRL-C to stop\n");
});
export default server;

16
server/src/app.js Normal file
View File

@@ -0,0 +1,16 @@
import express from 'express';
import boom from 'express-boom';
import '../config';
import routes from '@/http';
const app = express();
// Express configuration
app.set('port', process.env.PORT || 3000);
app.use(boom());
app.use(express.json());
routes(app);
export default app;

View File

@@ -0,0 +1,58 @@
import knexFactory from 'knex-factory';
import faker from 'faker';
import knex from '@/database/knex';
import { hashPassword } from '@/utils';
const factory = knexFactory(knex);
factory.define('user', 'users', async () => {
const hashedPassword = await hashPassword('admin');
return {
first_name: faker.name.firstName(),
last_name: faker.name.lastName(),
email: faker.internet.email(),
phone_number: faker.phone.phoneNumber(),
active: 1,
password: hashedPassword,
};
});
factory.define('account', 'accounts', async () => ({
name: faker.lorem.word(),
type: faker.lorem.word(),
description: faker.lorem.paragraph(),
}));
factory.define('item_category', 'items_categories', () => ({
label: faker.name.firstName(),
description: faker.lorem.text(),
parent_category_id: null,
}));
factory.define('item_metadata', 'items_metadata', async () => {
const item = await factory.create('item');
return {
key: faker.lorem.slug(),
value: faker.lorem.word(),
item_id: item.id,
};
});
factory.define('item', 'items', async () => {
const category = await factory.create('item_category');
const account = await factory.create('account');
return {
name: faker.lorem.word(),
note: faker.lorem.paragraph(),
cost_price: faker.random.number(),
sell_price: faker.random.number(),
cost_account_id: account.id,
sell_account_id: account.id,
category_id: category.id,
};
});
export default factory;

View File

@@ -0,0 +1,7 @@
import Knex from 'knex';
import knexfile from '@/../knexfile';
const config = knexfile[process.env.NODE_ENV];
const knex = Knex(config);
export default knex;

View File

@@ -0,0 +1,10 @@
exports.up = (knex) => knex.schema.createTable('roles', (table) => {
table.increments();
table.string('name');
table.string('description');
table.boolean('predefined').default(false);
table.timestamps();
});
exports.down = (knex) => knex.schema.dropTable('roles');

View File

@@ -0,0 +1,20 @@
exports.up = function(knex) {
return knex.schema.createTable('users', (table) => {
table.increments();
table.string('first_name');
table.string('last_name');
table.string('email').unique();
table.string('phone_number').unique();
table.string('password');
table.boolean('active');
table.integer('role_id').unique();
table.string('language');
table.date('last_login_at');
table.timestamps();
});
};
exports.down = function(knex) {
return knex.schema.dropTableIfExists('users');
};

View File

@@ -0,0 +1,16 @@
exports.up = function(knex) {
return knex.schema.createTable('oauth_tokens', table => {
table.increments();
table.string('access_token');
table.date('access_token_expires_on');
table.integer('client_id').unsigned();
table.string('refresh_token');
table.date('refresh_token_expires_on');
table.integer('user_id').unsigned().references('id').inTable('users');
});
};
exports.down = function(knex) {
return knex.schema.dropTableIfExists('oauth_tokens');
};

View File

@@ -0,0 +1,11 @@
exports.up = function(knex) {
return knex.schema.createTable('oauth_clients', table => {
table.increments();
table.integer('client_id').unsigned();
table.string('client_secret');
table.string('redirect_uri');
});
};
exports.down = (knex) => knex.schema.dropTableIfExists('oauth_clients');

View File

@@ -0,0 +1,11 @@
exports.up = function(knex) {
return knex.schema.createTable('settings', table => {
table.increments();
table.integer('user_id').unsigned().references('id').inTable('users');
table.string('key');
table.string('value');
});
};
exports.down = (knex) => knex.schema.dropTableIfExists('settings');

Some files were not shown because too many files have changed in this diff Show More