WIP Items module.
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/data
|
||||
29
.vscode/settings.json
vendored
Normal 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,
|
||||
|
||||
}
|
||||
@@ -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"],
|
||||
|
||||
@@ -38,6 +38,7 @@ module.exports = {
|
||||
props: true,
|
||||
ignorePropertyModificationsFor: [
|
||||
'state', // for vuex state
|
||||
's',
|
||||
'acc', // for reduce accumulators
|
||||
'e' // for e.returnvalue
|
||||
]
|
||||
|
||||
@@ -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,
|
||||
|
||||
43
client/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -9,3 +9,11 @@ export default {
|
||||
name: 'App',
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import 'style/style.scss';
|
||||
|
||||
body{
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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',
|
||||
|
||||
46
client/src/pages/Auth/ForgetPassword.vue
Normal 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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
31
client/src/pages/Dashboard/Customers/CustomersList.vue
Normal 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>
|
||||
@@ -26,8 +26,12 @@ export default {
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
#dashboard{
|
||||
.dashboard{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
&__content{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
3
client/src/pages/Dashboard/Products/ProductForm.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
9
client/src/pages/Dashboard/Products/ProductsList.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'products-list',
|
||||
}
|
||||
</script>
|
||||
@@ -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'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
|
||||
@@ -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 };
|
||||
|
||||
|
||||
77
client/src/store/modules/customers.js
Normal 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 };
|
||||
71
client/src/store/modules/items.js
Normal 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 };
|
||||
@@ -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 };
|
||||
|
||||
994
client/src/style/_element-variables.scss
Normal 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),
|
||||
);
|
||||
0
client/src/style/_reboot.scss
Normal file
3
client/src/style/style.scss
Normal file
@@ -0,0 +1,3 @@
|
||||
|
||||
@import 'element-variables';
|
||||
@import 'reboot';
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -0,0 +1,4 @@
|
||||
|
||||
export function roundTo(num, to = 2) {
|
||||
return +(Math.round(num + "e+" + to) + "e-" + to);
|
||||
}
|
||||
41
docker/.gitignore
vendored
Normal 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
@@ -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
@@ -0,0 +1,385 @@
|
||||

|
||||
|
||||
Docker Compose for Node projects with Node, MySQL, MongoDB, NGINX, Memcached, Redis, Certbot and RabbitMQ images
|
||||
|
||||

|
||||
|
||||
<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
@@ -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"]
|
||||
0
docker/apache/certs/.gitkeep
Normal file
100
docker/apache/httpd.conf
Normal 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>
|
||||
20
docker/apache/scripts/build-apache.sh
Normal 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
|
||||
31
docker/apache/scripts/run-openssl.sh
Normal 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
|
||||
|
||||
17
docker/apache/sites/node-https.template
Normal 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>
|
||||
14
docker/apache/sites/node.template
Normal 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>
|
||||
8
docker/certbot/Dockerfile
Normal 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
@@ -0,0 +1 @@
|
||||
*.pem
|
||||
0
docker/certbot/certs/.gitkeep
Normal file
0
docker/certbot/letsencrypt/.gitkeep
Normal file
0
docker/certbot/letsencrypt/.well-known/.gitkeep
Normal file
6
docker/certbot/scripts/run-certbot.sh
Normal 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
@@ -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
@@ -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
@@ -0,0 +1 @@
|
||||
nodock.io
|
||||
190
docker/docs/css/styles.css
Normal 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;
|
||||
}
|
||||
}
|
||||
BIN
docker/docs/images/box-icon.png
Normal file
|
After Width: | Height: | Size: 974 B |
BIN
docker/docs/images/favicon.png
Normal file
|
After Width: | Height: | Size: 609 B |
BIN
docker/docs/images/lightning-icon.png
Normal file
|
After Width: | Height: | Size: 724 B |
BIN
docker/docs/images/logo.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
docker/docs/images/nodock.jpg
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
docker/docs/images/osedea-logo.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
docker/docs/images/tech-logos.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
113
docker/docs/index.html
Normal 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 & 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>
|
||||
3
docker/memcached/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM memcached:1.4
|
||||
|
||||
CMD memcached
|
||||
9
docker/mongo/Dockerfile
Normal 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
@@ -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
@@ -0,0 +1,2 @@
|
||||
[mysqld]
|
||||
bind-address = 0.0.0.0
|
||||
23
docker/nginx/Dockerfile
Normal 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
|
||||
0
docker/nginx/certs/.gitkeep
Normal file
33
docker/nginx/nginx.conf
Normal 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;
|
||||
}
|
||||
20
docker/nginx/scripts/build-nginx.sh
Normal 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
|
||||
31
docker/nginx/scripts/run-openssl.sh
Normal 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
|
||||
|
||||
13
docker/nginx/sites/node-https.template
Normal 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};
|
||||
}
|
||||
}
|
||||
32
docker/nginx/sites/node.template
Normal 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
@@ -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
|
||||
21
docker/node/scripts/run-nodock.sh
Normal 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
|
||||
11
docker/postgresql/Dockerfile
Normal 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
|
||||
15
docker/rabbitmq/Dockerfile
Normal 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
|
||||
13
docker/rabbitmq/scripts/build-rabbitmq.sh
Normal 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
@@ -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
@@ -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
|
||||
3
docker/rethinkdb/Dockerfile
Normal file
@@ -0,0 +1,3 @@
|
||||
FROM rethinkdb
|
||||
|
||||
CMD ["rethinkdb", "--bind", "all"]
|
||||
39
docker/workspace/Dockerfile
Normal 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
|
||||
2
docker/workspace/crontab/root
Normal file
@@ -0,0 +1,2 @@
|
||||
# This is the root crontab file
|
||||
# * * * * * echo "Every Minute" >> /var/log/cron.log
|
||||
6
server/.babelrc
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
{
|
||||
"presets": ["@babel/preset-env"],
|
||||
"retainLines": true,
|
||||
"plugins": ["@babel/plugin-transform-runtime"]
|
||||
}
|
||||
14
server/.env
Normal 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
@@ -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
@@ -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
@@ -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
1
server/dist/bundle.js.map
vendored
Normal file
51
server/knexfile.js
Normal 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
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
31
server/scripts/run_test_db.sh
Normal 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}"
|
||||
@@ -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
@@ -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;
|
||||
58
server/src/database/factories/index.js
Normal 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;
|
||||
7
server/src/database/knex.js
Normal 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;
|
||||
@@ -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');
|
||||
@@ -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');
|
||||
};
|
||||
@@ -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');
|
||||
};
|
||||
@@ -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');
|
||||
@@ -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');
|
||||