瀏覽代碼

Integration to Requarks Core

NGPixel 8 年之前
父節點
當前提交
985761556c
共有 52 個文件被更改,包括 875 次插入1045 次删除
  1. 6 5
      agent.js
  2. 0 0
      assets/css/app.css
  3. 0 0
      assets/css/libs.css
  4. 0 0
      assets/css/login.css
  5. 32 0
      assets/fonts/core-icons.svg
  6. 二進制
      assets/fonts/core-icons.ttf
  7. 二進制
      assets/fonts/core-icons.woff
  8. 二進制
      assets/fonts/fontawesome-webfont.eot
  9. 二進制
      assets/fonts/fontawesome-webfont.ttf
  10. 二進制
      assets/fonts/fontawesome-webfont.woff
  11. 二進制
      assets/fonts/fontawesome-webfont.woff2
  12. 14 9
      client/scss/app.scss
  13. 0 24
      client/scss/components/_alerts.scss
  14. 0 10
      client/scss/layout/_base.scss
  15. 18 63
      client/scss/layout/_content.scss
  16. 0 100
      client/scss/layout/_fonts.scss
  17. 6 0
      client/scss/layout/_footer.scss
  18. 2 2
      client/scss/layout/_header.scss
  19. 31 0
      client/scss/libs/bulma/base/classes.scss
  20. 153 0
      client/scss/libs/bulma/base/helpers.scss
  21. 197 0
      client/scss/libs/bulma/components/nav.scss
  22. 123 0
      client/scss/libs/bulma/utilities/mixins.scss
  23. 3 3
      client/scss/libs/jquery-contextmenu.scss
  24. 2 279
      client/scss/login.scss
  25. 25 1
      controllers/admin.js
  26. 0 0
      core/core-client/scss/components/form.scss
  27. 4 0
      core/core-client/scss/components/sidebar.scss
  28. 64 50
      gulpfile.js
  29. 0 155
      libs/auth.js
  30. 0 56
      libs/config.js
  31. 12 1
      libs/markdown.js
  32. 0 64
      libs/mongo.js
  33. 0 57
      libs/rights.js
  34. 0 18
      libs/winston.js
  35. 2 4
      models/bruteforce.js
  36. 3 4
      models/entry.js
  37. 3 4
      models/upl-file.js
  38. 3 4
      models/upl-folder.js
  39. 3 4
      models/user.js
  40. 3 0
      package.json
  41. 12 10
      server.js
  42. 4 4
      views/common/header.pug
  43. 1 1
      views/layout.pug
  44. 0 69
      views/pages/account.pug
  45. 30 0
      views/pages/admin/_layout.pug
  46. 43 0
      views/pages/admin/profile.pug
  47. 6 0
      views/pages/admin/settings.pug
  48. 11 0
      views/pages/admin/stats.pug
  49. 15 0
      views/pages/admin/users.pug
  50. 4 6
      views/pages/edit.pug
  51. 4 6
      views/pages/source.pug
  52. 36 32
      views/pages/view.pug

+ 6 - 5
agent.js

@@ -4,15 +4,16 @@
 // Licensed under AGPLv3
 // ===========================================
 
-global.ROOTPATH = __dirname;
 global.PROCNAME = 'AGENT';
+global.ROOTPATH = __dirname;
+global.CORE_PATH = ROOTPATH + '/../core/';
+global.IS_DEBUG = process.env.NODE_ENV === 'development';
 
 // ----------------------------------------
 // Load Winston
 // ----------------------------------------
 
-var _isDebug = process.env.NODE_ENV === 'development';
-global.winston = require('./libs/winston')(_isDebug);
+global.winston = require(CORE_PATH + 'core-libs/winston')(IS_DEBUG);
 
 // ----------------------------------------
 // Load global modules
@@ -20,8 +21,8 @@ global.winston = require('./libs/winston')(_isDebug);
 
 winston.info('[AGENT] Background Agent is initializing...');
 
-var appconfig = require('./libs/config')('./config.yml');
-global.db = require('./libs/mongo').init(appconfig);
+var appconfig = require(CORE_PATH + 'core-libs/config')('./config.yml');
+global.db = require(CORE_PATH + 'core-libs/mongodb').init(appconfig);
 global.upl = require('./libs/uploads-agent').init(appconfig);
 global.git = require('./libs/git').init(appconfig);
 global.entries = require('./libs/entries').init(appconfig);

文件差異過大導致無法顯示
+ 0 - 0
assets/css/app.css


文件差異過大導致無法顯示
+ 0 - 0
assets/css/libs.css


文件差異過大導致無法顯示
+ 0 - 0
assets/css/login.css


文件差異過大導致無法顯示
+ 32 - 0
assets/fonts/core-icons.svg


二進制
assets/fonts/core-icons.ttf


二進制
assets/fonts/core-icons.woff


二進制
assets/fonts/fontawesome-webfont.eot


二進制
assets/fonts/fontawesome-webfont.ttf


二進制
assets/fonts/fontawesome-webfont.woff


二進制
assets/fonts/fontawesome-webfont.woff2


+ 14 - 9
client/scss/app.scss

@@ -1,17 +1,22 @@
-//@import './layout/_fonts';
-@import './layout/_base';
-@import './layout/_mixins';
+@import 'core-client/scss/core';
+@import 'core-client/scss/components/alert';
+@import 'core-client/scss/components/button';
+@import 'core-client/scss/components/form';
+@import 'core-client/scss/components/grid';
+@import 'core-client/scss/components/hero';
+@import 'core-client/scss/components/markdown-content';
+@import 'core-client/scss/components/modal';
+@import 'core-client/scss/components/nav';
+@import 'core-client/scss/components/sidebar';
+@import 'core-client/scss/components/typography';
 
-@import './libs/bulma/bulma';
 @import './libs/twemoji-awesome';
-@import './libs/animate.min.css';
 @import './libs/jquery-contextmenu';
 
-@import './components/_alerts';
-@import './components/_editor';
+//@import './components/_editor';
 
 @import './layout/_header';
 @import './layout/_footer';
-@import './layout/_content';
+//@import './layout/_content';
 
-@import './pages/_account';
+//@import './pages/_account';

+ 0 - 24
client/scss/components/_alerts.scss

@@ -1,24 +0,0 @@
-#alerts {
-	position: fixed;
-	top: 60px;
-	right: 10px;
-	width: 350px;
-	z-index: 10;
-	text-shadow: 1px 1px 0 rgba(0,0,0,0.1);
-
-	.notification {
-		animation: 0.5s ease slideInRight;
-		margin-top: 5px;
-
-		&.exit {
-			animation: 0.5s ease fadeOutRight;
-		}
-
-	}
-
-	h3 {
-		font-size: 16px;
-		font-size: 500;
-	}
-
-}

+ 0 - 10
client/scss/layout/_base.scss

@@ -1,10 +0,0 @@
-html {
-  box-sizing: border-box;
-}
-*, *:before, *:after {
-  box-sizing: inherit;
-}
-
-[v-cloak] {
-  display: none;
-}

+ 18 - 63
client/scss/layout/_content.scss

@@ -1,13 +1,10 @@
 
-#root {
-	padding-top: 52px;
-}
 
 #page-type-view > section {
 	transition: background-color .5s ease;
 
 	&.blurred {
-		background-color: $grey-lighter;
+		background-color: mc('grey', '50');
 	}
 
 }
@@ -36,26 +33,26 @@
 .mkcontent {
 
 	h1 {
-		border-bottom: 1px dotted $blue;
+		border-bottom: 1px dotted mc('blue', '500');
 		padding-bottom: 4px;
 		font-weight: 400;
-		color: desaturate($blue, 20%);
+		color: desaturate(mc('blue', '500'), 20%);
 	}
 
 	h2 {
-		border-bottom: 1px dotted $grey-light;
+		border-bottom: 1px dotted mc('grey', '100');
 		padding-bottom: 4px;
 		font-weight: 400;
-		color: desaturate($purple, 20%);
+		color: desaturate(mc('purple', '500'), 20%);
 	}
 
 	a.toc-anchor {
 		font-size: 80%;
-		color: $blue;
+		color: mc('blue', '500');
 		border-bottom: none;
 
 		&:visited {
-			color: $purple !important;
+			color: mc('purple', '500') !important;
 		}
 
 	}
@@ -70,7 +67,7 @@
 			font-style: normal;
 			font-weight: normal;
 			text-decoration: inherit;
-			color: $grey;
+			color: mc('grey', '500');
 			font-size: 14px;
 			position: absolute;
 			top: 0;
@@ -81,10 +78,10 @@
 
 	pre {
 		padding: 0;
-		font-family: $family-monospace;
+		font-family: $core-font-monospace;
 
 		> code {
-			box-shadow: inset 0 0 5px 0 $grey-light;
+			box-shadow: inset 0 0 5px 0 mc('grey', '100');
 			border-radius: 5px;
 		}
 
@@ -106,7 +103,7 @@
 	}
 
 	strong {
-		color: $grey-dark;
+		color: mc('grey', '700');
 	}
 
 	.twa {
@@ -114,18 +111,18 @@
 	}
 
 	table thead th {
-		background-color: $blue;
+		background-color: mc('blue', '500');
 		color: #FFF;
 		border-color: #FFF;
-		border-bottom-color: $blue;
-		border-top-color: $blue;
+		border-bottom-color: mc('blue', '500');
+		border-top-color: mc('blue', '500');
 
 		&:first-child {
-			border-left-color: $blue;
+			border-left-color: mc('blue', '500');
 		}
 
 		&:last-child {
-			border-right-color: $blue;
+			border-right-color: mc('blue', '500');
 		}
 
 	}
@@ -133,58 +130,16 @@
 }
 
 .content a:not(.button):visited {
-  color: $turquoise;
+  color: mc('teal', '500');
   font-weight: 500;
 }
 
 code {
 	font-weight: 500;
-	color: $purple;
+	color: mc('purple', '500');
 }
 
 p code {
 	padding: 2px 5px;
 	border-radius: 4px;
-}
-
-.modal {
-	align-items: flex-start;
-}
-.modal-background {
-	animation: 0.4s ease fadeIn;
-}
-.modal-content {
-	animation: 0.4s ease slideInDown;	
-}
-
-.card-header {
-	background-color: $turquoise;
-
-	&.is-warning {
-		background-color: $orange;
-	}
-
-	&.is-danger {
-		background-color: $red;
-	}
-
-	&.is-info {
-		background-color: $purple;
-	}
-
-}
-
-.card-header-title {
-	color: #FFF;
-	font-weight: 400;
-	font-size: 16px;
-	padding: 10px 20px;
-}
-
-.modal-content .card-footer-item {
-	font-weight: 500;
-}
-
-.modal-content .card-footer-item.featured {
-	animation: flash 4s ease 0 infinite;
 }

+ 0 - 100
client/scss/layout/_fonts.scss

@@ -1,100 +0,0 @@
-
-// Roboto
-// by Christian Robertson
-// Apache 2.0 License
-
-// -> Thin
-
-@font-face {
-	font-family: 'Roboto';
-	src: url('/fonts/Roboto-Thin.woff') format('woff');
-	font-weight: 100;
-	font-style: normal;
-}
-
-@font-face {
-	font-family: 'Roboto';
-	src: url('/fonts/Roboto-ThinItalic.woff') format('woff');
-	font-weight: 100;
-	font-style: italic;
-}
-
-// -> Light
-
-@font-face {
-	font-family: 'Roboto';
-	src: url('/fonts/Roboto-Light.woff') format('woff');
-	font-weight: 300;
-	font-style: normal;
-}
-
-@font-face {
-	font-family: 'Roboto';
-	src: url('/fonts/Roboto-LightItalic.woff') format('woff');
-	font-weight: 300;
-	font-style: italic;
-}
-
-// -> Regular
-
-@font-face {
-	font-family: 'Roboto';
-	src: url('/fonts/Roboto-Regular.woff') format('woff');
-	font-weight: 400;
-	font-style: normal;
-}
-
-@font-face {
-	font-family: 'Roboto';
-	src: url('/fonts/Roboto-RegularItalic.woff') format('woff');
-	font-weight: 400;
-	font-style: italic;
-}
-
-// -> Medium
-
-@font-face {
-	font-family: 'Roboto';
-	src: url('/fonts/Roboto-Medium.woff') format('woff');
-	font-weight: 500;
-	font-style: normal;
-}
-
-@font-face {
-	font-family: 'Roboto';
-	src: url('/fonts/Roboto-MediumItalic.woff') format('woff');
-	font-weight: 500;
-	font-style: italic;
-}
-
-// -> Bold
-
-@font-face {
-	font-family: 'Roboto';
-	src: url('/fonts/Roboto-Bold.woff') format('woff');
-	font-weight: 700;
-	font-style: normal;
-}
-
-@font-face {
-	font-family: 'Roboto';
-	src: url('/fonts/Roboto-BoldItalic.woff') format('woff');
-	font-weight: 700;
-	font-style: italic;
-}
-
-// -> Black
-
-@font-face {
-	font-family: 'Roboto';
-	src: url('/fonts/Roboto-Black.woff') format('woff');
-	font-weight: 900;
-	font-style: normal;
-}
-
-@font-face {
-	font-family: 'Roboto';
-	src: url('/fonts/Roboto-BlackItalic.woff') format('woff');
-	font-weight: 900;
-	font-style: italic;
-}

+ 6 - 0
client/scss/layout/_footer.scss

@@ -0,0 +1,6 @@
+.footer {
+	background-color: mc('light-blue', '600');
+	color: #FFF;
+	text-align: center;
+	padding: 25px;
+}

+ 2 - 2
client/scss/layout/_header.scss

@@ -13,7 +13,7 @@
 
 h2.nav-item {
 	font-size: 150%;
-	color: $orange;
+	color: mc('orange', '500');
 }
 
 #notifload {
@@ -26,7 +26,7 @@ h2.nav-item {
 
 	&::before {
 		content: " ";
-		@include spinner($orange,0.5s,24px);
+		@include spinner(mc('orange', '500'),0.5s,24px);
 	}
 
 	&.active {

+ 31 - 0
client/scss/libs/bulma/base/classes.scss

@@ -0,0 +1,31 @@
+.block {
+  &:not(:last-child) {
+    margin-bottom: 20px;
+  }
+}
+
+.container {
+  position: relative;
+
+  @include desktop {
+    margin: 0 auto;
+    max-width: 960px;
+
+    // Modifiers
+    &.is-fluid {
+      margin: 0 20px;
+      max-width: none;
+    }
+  }
+
+
+  @include widescreen {
+    max-width: 1200px;
+  }
+}
+
+.fa {
+  font-size: 21px;
+  text-align: center;
+  vertical-align: top;
+}

+ 153 - 0
client/scss/libs/bulma/base/helpers.scss

@@ -0,0 +1,153 @@
+// Display
+
+$displays: "block" "flex" "inline" "inline-block" "inline-flex";
+
+@each $display in $displays {
+  .is-#{$display} {
+    display: #{$display};
+  }
+
+  .is-#{$display}-mobile {
+    @include mobile {
+      display: #{$display} !important;
+    }
+  }
+
+  .is-#{$display}-tablet {
+    @include tablet {
+      display: #{$display} !important;
+    }
+  }
+
+  .is-#{$display}-tablet-only {
+    @include tablet-only {
+      display: #{$display} !important;
+    }
+  }
+
+  .is-#{$display}-touch {
+    @include touch {
+      display: #{$display} !important;
+    }
+  }
+
+  .is-#{$display}-desktop {
+    @include desktop {
+      display: #{$display} !important;
+    }
+  }
+
+  .is-#{$display}-desktop-only {
+    @include desktop-only {
+      display: #{$display} !important;
+    }
+  }
+
+  .is-#{$display}-widescreen {
+    @include widescreen {
+      display: #{$display} !important;
+    }
+  }
+}
+
+// Float
+
+.is-clearfix {
+  @include clearfix;
+}
+
+.is-pulled-left {
+  float: left;
+}
+
+.is-pulled-right {
+  float: right;
+}
+
+// Overflow
+
+.is-clipped {
+  overflow: hidden !important;
+}
+
+// Overlay
+
+.is-overlay {
+  @include overlay;
+}
+
+// Text
+
+.has-text-centered {
+  text-align: center;
+}
+
+.has-text-left {
+  text-align: left;
+}
+
+.has-text-right {
+  text-align: right;
+}
+
+// Visibility
+
+.is-hidden {
+  display: none !important;
+}
+
+.is-hidden-mobile {
+  @include mobile {
+    display: none !important;
+  }
+}
+
+.is-hidden-tablet {
+  @include tablet {
+    display: none !important;
+  }
+}
+
+.is-hidden-tablet-only {
+  @include tablet-only {
+    display: none !important;
+  }
+}
+
+.is-hidden-touch {
+  @include touch {
+    display: none !important;
+  }
+}
+
+.is-hidden-desktop {
+  @include desktop {
+    display: none !important;
+  }
+}
+
+.is-hidden-desktop-only {
+  @include desktop-only {
+    display: none !important;
+  }
+}
+
+.is-hidden-widescreen {
+  @include widescreen {
+    display: none !important;
+  }
+}
+
+// Other
+
+.is-disabled {
+  pointer-events: none;
+}
+
+.is-marginless {
+  margin: 0 !important;
+}
+
+.is-unselectable {
+  @extend .unselectable;
+}

+ 197 - 0
client/scss/libs/bulma/components/nav.scss

@@ -0,0 +1,197 @@
+// Components
+
+.nav-toggle {
+  @extend .hamburger;
+
+  // Responsiveness
+  @include tablet {
+    display: none;
+  }
+}
+
+.nav-item {
+  align-items: center;
+  display: flex;
+  justify-content: center;
+  padding: 10px;
+
+  a {
+    flex-grow: 1;
+  }
+
+  img {
+    max-height: 24px;
+  }
+
+  .button + .button {
+    margin-left: 10px;
+  }
+
+  .tag {
+    &:first-child {
+      margin-right: 5px;
+    }
+
+    &:last-child {
+      margin-left: 5px;
+    }
+  }
+
+  // Responsiveness
+  @include mobile {
+    justify-content: flex-start;
+  }
+}
+
+.nav-item a,
+a.nav-item {
+  color: $text;
+
+  &:hover {
+    color: $link-hover;
+  }
+
+  // Modifiers
+  &.is-active {
+    color: $link-active;
+  }
+
+  &.is-tab {
+    border-bottom: 1px solid transparent;
+    border-top: 1px solid transparent;
+    padding-left: 12px;
+    padding-right: 12px;
+
+    &:hover {
+      border-bottom: 1px solid $link;
+      border-top: 1px solid transparent;
+    }
+
+    &.is-active {
+      border-bottom: 3px solid $link;
+      border-top: 3px solid transparent;
+      color: $link;
+    }
+  }
+}
+
+// Containers
+
+.nav-menu {
+  // Responsiveness
+  @include mobile {
+    background-color: $white;
+    box-shadow: 0 4px 7px rgba($black, 0.1);
+    left: 0;
+    display: none;
+    right: 0;
+    top: 100%;
+    position: absolute;
+
+    .nav-item {
+      border-top: 1px solid rgba($border, 0.5);
+      padding: 10px;
+    }
+
+    &.is-active {
+      display: block;
+    }
+  }
+
+
+  @include tablet-only {
+    padding-right: 20px;
+  }
+}
+
+.nav-left {
+  align-items: stretch;
+  display: flex;
+  flex-basis: 0;
+  flex-grow: 1;
+  justify-content: flex-start;
+  overflow: hidden;
+  overflow-x: auto;
+  white-space: nowrap;
+}
+
+.nav-center {
+  align-items: stretch;
+  display: flex;
+  justify-content: center;
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.nav-right {
+  // Responsiveness
+  @include tablet {
+    align-items: stretch;
+    display: flex;
+    flex-basis: 0;
+    flex-grow: 1;
+    justify-content: flex-end;
+  }
+}
+
+// Main container
+
+.nav {
+  align-items: stretch;
+  background-color: $white;
+  display: flex;
+  min-height: $nav-height;
+  position: relative;
+  text-align: center;
+  z-index: 2;
+
+  & > .container {
+    align-items: stretch;
+    display: flex;
+    min-height: $nav-height;
+    width: 100%;
+
+    & > .nav-left {
+      & > .nav-item:first-child:not(.is-tab) {
+        padding-left: 0;
+      }
+    }
+
+    & > .nav-right {
+      & > .nav-item:last-child:not(.is-tab) {
+        padding-right: 0;
+      }
+    }
+  }
+
+  .container > & {
+    & > .nav-left {
+      & > .nav-item:first-child:not(.is-tab) {
+        padding-left: 0;
+      }
+    }
+
+    & > .nav-right {
+      & > .nav-item:last-child:not(.is-tab) {
+        padding-right: 0;
+      }
+    }
+  }
+
+  // Modifiers
+  &.has-shadow {
+    box-shadow: 0 2px 3px rgba($black, 0.1);
+  }
+
+  // Responsiveness
+  @include touch {
+    & > .container,
+    .container > & {
+      & > .nav-left {
+        & > .nav-item.is-brand:first-child {
+          padding-left: 20px;
+        }
+      }
+    }
+  }
+}

+ 123 - 0
client/scss/libs/bulma/utilities/mixins.scss

@@ -0,0 +1,123 @@
+@mixin arrow($color) {
+  border: 1px solid $color;
+  border-right: 0;
+  border-top: 0;
+  content: " ";
+  display: block;
+  height: 7px;
+  pointer-events: none;
+  position: absolute;
+  transform: rotate(-45deg);
+  width: 7px;
+}
+
+@mixin clearfix {
+  &:after {
+    clear: both;
+    content: " ";
+    display: table;
+  }
+}
+
+@mixin center($size) {
+  left: 50%;
+  margin-left: -($size / 2);
+  margin-top: -($size / 2);
+  position: absolute;
+  top: 50%;
+}
+
+@mixin fa($size, $dimensions) {
+  display: inline-block;
+  font-size: $size;
+  height: $dimensions;
+  line-height: $dimensions;
+  text-align: center;
+  vertical-align: top;
+  width: $dimensions;
+}
+
+@mixin overlay($offset: 0) {
+  bottom: $offset;
+  left: $offset;
+  position: absolute;
+  right: $offset;
+  top: $offset;
+}
+
+@mixin placeholder {
+  $placeholders: ":-moz" ":-webkit-input" "-moz" "-ms-input";
+
+  @each $placeholder in $placeholders {
+    &:#{$placeholder}-placeholder {
+      @content;
+    }
+  }
+}
+
+@mixin replace($background, $width, $height) {
+  background-color: $background;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: $width $height;
+  display: block;
+  height: $height;
+  outline: none;
+  overflow: hidden;
+  text-indent: -290486px;
+  width: $width;
+}
+
+@mixin from($device) {
+  @media screen and (min-width: $device) {
+    @content;
+  }
+}
+
+@mixin until($device) {
+  @media screen and (max-width: $device - 1px) {
+    @content;
+  }
+}
+
+@mixin mobile {
+  @media screen and (max-width: $tablet - 1px) {
+    @content;
+  }
+}
+
+@mixin tablet {
+  @media screen and (min-width: $tablet) {
+    @content;
+  }
+}
+
+@mixin tablet-only {
+  @media screen and (min-width: $tablet) and (max-width: $desktop - 1px) {
+    @content;
+  }
+}
+
+@mixin touch {
+  @media screen and (max-width: $desktop - 1px) {
+    @content;
+  }
+}
+
+@mixin desktop {
+  @media screen and (min-width: $desktop) {
+    @content;
+  }
+}
+
+@mixin desktop-only {
+  @media screen and (min-width: $desktop) and (max-width: $widescreen - 1px) {
+    @content;
+  }
+}
+
+@mixin widescreen {
+  @media screen and (min-width: $widescreen) {
+    @content;
+  }
+}

+ 3 - 3
client/scss/libs/jquery-contextmenu.scss

@@ -29,7 +29,7 @@
   font-style: normal;
   font-weight: normal;
   line-height: 1;
-  color: $primary;
+  color: mc('blue', '500');
   text-align: center;
   -webkit-transform: translateY(-50%);
       -ms-transform: translateY(-50%);
@@ -57,7 +57,7 @@
   font-size: 14px;
   list-style-type: none;
   background: #fff;
-  border: 1px solid $primary;
+  border: 1px solid mc('blue', '500');
   border-radius: .2em;
   -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .25);
           box-shadow: 0 2px 5px rgba(0, 0, 0, .25);
@@ -85,7 +85,7 @@
 .context-menu-item.context-menu-hover {
   color: #fff;
   cursor: pointer; 
-  background-color: $primary;
+  background-color: mc('blue', '500');
 }
 
 .context-menu-item.context-menu-disabled {

+ 2 - 279
client/scss/login.scss

@@ -1,279 +1,2 @@
-@import './layout/_base';
-@import './layout/_mixins';
-
-@import './libs/animate.min.css';
-
-body {
-	padding: 0;
-	margin: 0;
-	font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
-	font-size: 14px;
-}
-
-a {
-	color: #FFF;
-	transition: color 0.4s ease;
-	text-decoration: none;
-
-	&:hover {
-		color: #FB8C00;
-		text-decoration: underline;
-	}
-
-}
-
-#bg {
-	position: fixed;
-	top: 0;
-	left: 0;
-	width: 100%;
-	height: 100%;
-	z-index: 1;
-	background-color: #000;
-
-	> div {
-		background-size: cover;
-		background-position: center center;
-		width: 100%;
-		height: 100%;
-		position: absolute;
-		top: 0;
-		left: 0;
-		opacity: 0;
-		visibility: hidden;
-		transition: opacity 3s ease, visibility 3s;
-		animation: bg 30s linear infinite;
-
-		&:nth-child(1) {
-			animation-delay: 10s;
-		}
-
-		&:nth-child(2) {
-			animation-delay: 20s;
-		}
-
-	}
-
-}
-
-#root {
-	position: fixed;
-	top: 15vh;
-	left: 10vw;
-	z-index: 2;
-	color: #FFF;
-	display: flex;
-	flex-direction: column;
-
-	h1 {
-		font-size: 4rem;
-		color: #FFF;
-		padding: 0;
-		margin: 0;
-		animation: headerIntro 3s ease;
-	}
-
-	h2 {
-		font-size: 1.5rem;
-		font-weight: normal;
-		color: rgba(255,255,255,0.7);
-		padding: 0;
-		margin: 0 0 25px 0;
-		animation: headerIntro 3s ease;
-	}
-
-	h3 {
-		font-size: 1.25rem;
-		font-weight: normal;
-		color: #FB8C00;
-		padding: 0;
-		margin: 0;
-		animation: shake 1s ease;
-
-		> .fa {
-			margin-right: 7px;
-		}
-
-	}
-
-	h4 {
-		font-size: 0.8rem;
-		font-weight: normal;
-		color: rgba(255,255,255,0.7);
-		padding: 0;
-		margin: 0 0 15px 0;
-		animation: fadeIn 3s ease;
-	}
-
-	form {
-		display: flex;
-		flex-direction: column;
-	}
-
-	input[type=text], input[type=password] {
-		width: 350px;
-		max-width: 80vw;
-		border: 1px solid rgba(255,255,255,0.3);
-		border-radius: 3px;
-		background-color: rgba(0,0,0,0.2);
-		padding: 0 15px;
-		height: 40px;
-		margin: 0 0 10px 0;
-		color: #FFF;
-		font-weight: bold;
-		font-size: 14px;
-		transition: all 0.4s ease;
-
-		&:focus {
-			outline: none;
-			border-color: #FB8C00;
-		}
-
-	}
-
-	button {
-		background-color: #FB8C00;
-		color: #FFF;
-		border: 1px solid lighten(#FB8C00, 10%);
-		border-radius: 3px;
-		height: 40px;
-		width: 125px;
-		padding: 0;
-		font-weight: bold;
-		margin: 15px 0 0 0;
-		transition: all 0.4s ease;
-		cursor: pointer;
-
-		&:focus {
-			outline: none;
-			border-color: #FFF;
-		}
-
-		&:hover {
-			background-color: darken(#FB8C00, 10%);
-		}
-
-	}
-
-	#social {
-		margin-top: 25px;
-
-		> span {
-			display: block;
-			font-weight: bold;
-			color: rgba(255,255,255,0.7);
-		}
-
-		button {
-			margin-right: 5px;
-			width: auto;
-			padding: 0 15px;
-
-			> i {
-				margin-right: 10px;
-				font-size: 16px;
-			}
-
-			&.ms {
-				background-color: #009688;
-				border-color: lighten(#009688, 10%);
-
-				&:focus {
-					border-color: #FFF;
-				}
-
-				&:hover {
-					background-color: darken(#009688, 10%);
-				}
-
-			}
-
-			&.google {
-				background-color: #2196F3;
-				border-color: lighten(#2196F3, 10%);
-
-				&:focus {
-					border-color: #FFF;
-				}
-
-				&:hover {
-					background-color: darken(#2196F3, 10%);
-				}
-
-			}
-
-			&.facebook {
-				background-color: #673AB7;
-				border-color: lighten(#673AB7, 10%);
-
-				&:focus {
-					border-color: #FFF;
-				}
-
-				&:hover {
-					background-color: darken(#673AB7, 10%);
-				}
-
-			}
-
-		}
-
-	}
-
-}
-
-#copyright {
-	display: flex;
-	align-items: center;
-	justify-content: flex-start;
-	position: absolute;
-	left: 10vw;
-	bottom: 10vh;
-	z-index: 2;
-	color: rgba(255,255,255,0.5);
-	font-weight: bold;
-
-	.icon {
-		font-size: 1.2rem;
-		margin: 0 8px;
-	}
-
-	a {
-		opacity: 0.75;
-	}
-
-}
-
-@include keyframes(bg) {
-	0% {
-		@include prefix(transform, scale(1,1));
-		visibility: visible;
-		opacity: 0;
-	},
-	5% {
-		opacity: 0.5;
-	},
-	33% {
-		opacity: 0.5;
-	},
-	38% {
-		@include prefix(transform, scale(1.2, 1.2));
-		opacity: 0;
-	},
-	39% {
-		visibility: hidden;
-	}
-	100% {
-		visibility: hidden;
-		opacity: 0;
-	}
-}
-
-@include keyframes(headerIntro) {
-	0% {
-		opacity: 0;
-	}
-	100% {
-		opacity: 1;
-	}
-}
+@import 'core-client/scss/core';
+@import 'core-client/scss/pages/login';

+ 25 - 1
controllers/admin.js

@@ -2,6 +2,7 @@
 
 var express = require('express');
 var router = express.Router();
+const Promise = require('bluebird');
 
 /**
  * Admin
@@ -11,7 +12,30 @@ router.get('/', (req, res) => {
 });
 
 router.get('/profile', (req, res) => {
-	res.render('pages/account.pug');
+	res.render('pages/admin/profile', { adminTab: 'profile' });
+});
+
+router.get('/stats', (req, res) => {
+	Promise.all([
+		db.Entry.count(),
+		db.UplFile.count(),
+		db.User.count()
+	]).spread((totalEntries, totalUploads, totalUsers) => {
+		return res.render('pages/admin/stats', {
+			totalEntries, totalUploads, totalUsers,
+			adminTab: 'stats'
+		}) || true;
+	}).catch((err) => {
+		throw err;
+	});
+});
+
+router.get('/users', (req, res) => {
+	res.render('pages/admin/users', { adminTab: 'users' });
+});
+
+router.get('/settings', (req, res) => {
+	res.render('pages/admin/settings', { adminTab: 'settings' });
 });
 
 module.exports = router;

+ 0 - 0
core/core-client/scss/components/form.scss


+ 4 - 0
core/core-client/scss/components/sidebar.scss

@@ -0,0 +1,4 @@
+
+.sidebar {
+	background-color: #FFF;
+}

+ 64 - 50
gulpfile.js

@@ -1,4 +1,5 @@
 var gulp = require("gulp");
+var watch = require('gulp-watch');
 var merge = require('merge-stream');
 var babel = require("gulp-babel");
 var uglify = require('gulp-uglify');
@@ -18,48 +19,59 @@ var include = require("gulp-include");
  * @type       {Object}
  */
 var paths = {
-	scriptlibs: [
-		'./node_modules/socket.io-client/socket.io.js',
-		'./node_modules/jquery/dist/jquery.min.js',
-		'./node_modules/vue/dist/vue.min.js',
-		'./node_modules/jquery-smooth-scroll/jquery.smooth-scroll.min.js',
-		'./node_modules/jquery-simple-upload/simpleUpload.min.js',
-		'./node_modules/jquery-contextmenu/dist/jquery.contextMenu.min.js',
-		'./node_modules/sticky-js/dist/sticky.min.js',
-		'./node_modules/simplemde/dist/simplemde.min.js',
-		'./node_modules/ace-builds/src-min-noconflict/ace.js',
-		'./node_modules/ace-builds/src-min-noconflict/ext-modelist.js',
-		'./node_modules/ace-builds/src-min-noconflict/mode-markdown.js',
-		'./node_modules/ace-builds/src-min-noconflict/theme-tomorrow_night.js',
-		'./node_modules/filesize.js/dist/filesize.min.js',
-		'./node_modules/lodash/lodash.min.js'
-	],
-	scriptlibs_acemodes: [
-		'./node_modules/ace-builds/src-min-noconflict/mode-*.js',
-		'!./node_modules/ace-builds/src-min-noconflict/mode-markdown.js'
-	],
-	scriptapps: [
-		'./client/js/*.js'
-	],
-	scriptapps_watch: [
-		'./client/js/**/*.js'
-	],
-	csslibs: [
-		'./node_modules/font-awesome/css/font-awesome.min.css',
-		'./node_modules/highlight.js/styles/default.css',
-		'./node_modules/simplemde/dist/simplemde.min.css'
-	],
-	cssapps: [
-		'./client/scss/*.scss'
-	],
-	cssapps_watch: [
-		'./client/scss/**/*.scss'
-	],
+	scripts: {
+		combine: [
+			'./node_modules/socket.io-client/socket.io.js',
+			'./node_modules/jquery/dist/jquery.min.js',
+			'./node_modules/vue/dist/vue.min.js',
+			'./node_modules/jquery-smooth-scroll/jquery.smooth-scroll.min.js',
+			'./node_modules/jquery-simple-upload/simpleUpload.min.js',
+			'./node_modules/jquery-contextmenu/dist/jquery.contextMenu.min.js',
+			'./node_modules/sticky-js/dist/sticky.min.js',
+			'./node_modules/simplemde/dist/simplemde.min.js',
+			'./node_modules/ace-builds/src-min-noconflict/ace.js',
+			'./node_modules/ace-builds/src-min-noconflict/ext-modelist.js',
+			'./node_modules/ace-builds/src-min-noconflict/mode-markdown.js',
+			'./node_modules/ace-builds/src-min-noconflict/theme-tomorrow_night.js',
+			'./node_modules/filesize.js/dist/filesize.min.js',
+			'./node_modules/lodash/lodash.min.js'
+		],
+		ace: [
+			'./node_modules/ace-builds/src-min-noconflict/mode-*.js',
+			'!./node_modules/ace-builds/src-min-noconflict/mode-markdown.js'
+		],
+		compile: [
+			'./client/js/*.js'
+		],
+		watch: [
+			'./client/js/**/*.js'
+		]
+	},
+	css: {
+		combine: [
+			'./node_modules/font-awesome/css/font-awesome.min.css',
+			'./node_modules/highlight.js/styles/tomorrow.css',
+			'./node_modules/simplemde/dist/simplemde.min.css'
+		],
+		compile: [
+			'./client/scss/*.scss'
+		],
+		includes: [
+			'../core',
+			//'./node_modules/requarks-core'
+		],
+		watch: [
+			'./client/scss/**/*.scss',
+			'../core/core-client/scss/**/*.scss'
+		]
+	},
 	fonts: [
 		'./node_modules/font-awesome/fonts/*-webfont.*',
-		'!./node_modules/font-awesome/fonts/*-webfont.svg'
+		'!./node_modules/font-awesome/fonts/*-webfont.svg',
+		//'../node_modules/requarks-core/core-client/fonts/**/*'
+		'../core/core-client/fonts/**/*'
 	],
-	deploypackage: [
+	deploy: [
 		'./**/*',
 		'!node_modules', '!node_modules/**',
 		'!coverage', '!coverage/**',
@@ -77,7 +89,7 @@ var paths = {
 /**
  * TASK - Starts server in development mode
  */
-gulp.task('server', ['scripts', 'css'/*, 'fonts'*/], function() {
+gulp.task('server', ['scripts', 'css', 'fonts'], function() {
 	nodemon({
 		script: './server',
 		ignore: ['assets/', 'client/', 'data/', 'repo/', 'tests/'],
@@ -98,12 +110,12 @@ gulp.task("scripts-libs", function () {
 
 	return merge(
 
-		gulp.src(paths.scriptlibs)
+		gulp.src(paths.scripts.combine)
 		.pipe(concat('libs.js', {newLine: ';\n'}))
 		.pipe(uglify({ mangle: false }))
 		.pipe(gulp.dest("./assets/js")),
 
-		gulp.src(paths.scriptlibs_acemodes)
+		gulp.src(paths.scripts.ace)
 		.pipe(gulp.dest("./assets/js/ace"))
 
 	);
@@ -115,7 +127,7 @@ gulp.task("scripts-libs", function () {
  */
 gulp.task("scripts-app", function () {
 
-	return gulp.src(paths.scriptapps)
+	return gulp.src(paths.scripts.compile)
 	.pipe(plumber())
 	.pipe(include({ extensions: "js" }))
 	.pipe(babel())
@@ -134,7 +146,7 @@ gulp.task("css", ['css-libs', 'css-app']);
  * TASK - Combine css libraries
  */
 gulp.task("css-libs", function () {
-	return gulp.src(paths.csslibs)
+	return gulp.src(paths.css.combine)
 	.pipe(plumber())
 	.pipe(concat('libs.css'))
 	.pipe(cleanCSS({ keepSpecialComments: 0 }))
@@ -146,9 +158,9 @@ gulp.task("css-libs", function () {
  * TASK - Combine app css
  */
 gulp.task("css-app", function () {
-	return gulp.src(paths.cssapps)
+	return gulp.src(paths.css.compile)
 	.pipe(plumber())
-	.pipe(sass())
+	.pipe(sass.sync({ includePaths: paths.css.includes }))
 	.pipe(cleanCSS({ keepSpecialComments: 0 }))
 	.pipe(plumber.stop())
 	.pipe(gulp.dest("./assets/css"));
@@ -166,8 +178,10 @@ gulp.task("fonts", function () {
  * TASK - Start dev watchers
  */
 gulp.task('watch', function() {
-	gulp.watch([paths.scriptapps_watch], ['scripts-app']);
-	gulp.watch([paths.cssapps_watch], ['css-app']);
+	return merge(
+		watch(paths.scripts.watch, {base: './'}, function() { return gulp.start('scripts-app'); }),
+		watch(paths.css.watch, {base: './'}, function() { return gulp.start('css-app'); })
+	);
 });
 
 /**
@@ -179,11 +193,11 @@ gulp.task('default', ['watch', 'server']);
  * TASK - Creates deployment packages
  */
 gulp.task('deploy', ['scripts', 'css', 'fonts'], function() {
-	var zipStream = gulp.src(paths.deploypackage)
+	var zipStream = gulp.src(paths.deploy)
 		.pipe(zip('requarks-wiki.zip'))
 		.pipe(gulp.dest('dist'));
 
-	var targzStream = gulp.src(paths.deploypackage)
+	var targzStream = gulp.src(paths.deploy)
 		.pipe(tar('requarks-wiki.tar'))
 		.pipe(gzip())
 		.pipe(gulp.dest('dist'));

+ 0 - 155
libs/auth.js

@@ -1,155 +0,0 @@
-"use strict";
-
-const LocalStrategy = require('passport-local').Strategy;
-const GoogleStrategy = require('passport-google-oauth20').Strategy;
-const WindowsLiveStrategy = require('passport-windowslive').Strategy;
-const FacebookStrategy = require('passport-facebook').Strategy;
-const _ = require('lodash');
-
-module.exports = function(passport, appconfig) {
-
-	// Serialization user methods
-
-	passport.serializeUser(function(user, done) {
-		done(null, user._id);
-	});
-
-	passport.deserializeUser(function(id, done) {
-		db.User.findById(id).then((user) => {
-			if(user) {
-				done(null, user);
-			} else {
-				done(new Error('User not found.'), null);
-			}
-			return true;
-		}).catch((err) => {
-			done(err, null);
-		});
-	});
-
-	// Local Account
-
-	if(appconfig.auth.local && appconfig.auth.local.enabled) {
-
-		passport.use('local',
-			new LocalStrategy({
-				usernameField : 'email',
-				passwordField : 'password'
-			},
-			(uEmail, uPassword, done) => {
-				db.User.findOne({ email: uEmail, provider: 'local' }).then((user) => {
-					if(user) {
-						return user.validatePassword(uPassword).then(() => {
-							return done(null, user) || true;
-						}).catch((err) => {
-							 return done(err, null);
-						});
-					} else {
-						return done(new Error('Invalid Login'), null);
-					}
-				}).catch((err) => {
-					done(err, null) ;
-				});
-			}
-		));
-
-	}
-
-	// Google ID
-
-	if(appconfig.auth.google && appconfig.auth.google.enabled) {
-
-		passport.use('google',
-			new GoogleStrategy({
-				clientID: appconfig.auth.google.clientId,
-				clientSecret: appconfig.auth.google.clientSecret,
-				callbackURL: appconfig.host + '/login/google/callback'
-		  },
-		  (accessToken, refreshToken, profile, cb) => {
-		  	db.User.processProfile(profile).then((user) => {
-		  		return cb(null, user) || true;
-		  	}).catch((err) => {
-		  		return cb(err, null) || true;
-		  	});
-		  }
-		));
-
-	}
-
-	// Microsoft Accounts
-
-	if(appconfig.auth.microsoft && appconfig.auth.microsoft.enabled) {
-
-		passport.use('windowslive',
-			new WindowsLiveStrategy({
-				clientID: appconfig.auth.microsoft.clientId,
-				clientSecret: appconfig.auth.microsoft.clientSecret,
-				callbackURL: appconfig.host + '/login/ms/callback'
-		  },
-		  function(accessToken, refreshToken, profile, cb) {
-		  	db.User.processProfile(profile).then((user) => {
-		  		return cb(null, user) || true;
-		  	}).catch((err) => {
-		  		return cb(err, null) || true;
-		  	});
-		  }
-		));
-
-	}
-
-	// Facebook
-
-	if(appconfig.auth.facebook && appconfig.auth.facebook.enabled) {
-
-		passport.use('facebook',
-			new FacebookStrategy({
-				clientID: appconfig.auth.facebook.clientId,
-				clientSecret: appconfig.auth.facebook.clientSecret,
-				callbackURL: appconfig.host + '/login/facebook/callback',
-				profileFields: ['id', 'displayName', 'email']
-		  },
-		  function(accessToken, refreshToken, profile, cb) {
-		  	db.User.processProfile(profile).then((user) => {
-		  		return cb(null, user) || true;
-		  	}).catch((err) => {
-		  		return cb(err, null) || true;
-		  	});
-		  }
-		));
-
-	}
-
-	// Check for admin access
-
-	db.onReady.then(() => {
-
-		db.User.count().then((c) => {
-			if(c < 1) {
-				winston.info('[' + PROCNAME + '][AUTH] No administrator account found. Creating a new one...');
-				db.User.hashPassword('admin123').then((pwd) => {
-					return db.User.create({
-						provider: 'local',
-						email: appconfig.admin,
-						name: "Administrator",
-						password: pwd,
-						rights: [{
-							role: 'admin',
-							path: '/',
-							exact: false,
-							deny: false
-						}]
-					});
-				}).then(() => {
-					winston.info('[' + PROCNAME + '][AUTH] Administrator account created successfully!');
-				}).catch((err) => {
-					winston.error('[' + PROCNAME + '][AUTH] An error occured while creating administrator account:');
-					winston.error(err);
-				});
-			}
-		});
-		
-		return true;
-
-	});
-
-};

+ 0 - 56
libs/config.js

@@ -1,56 +0,0 @@
-"use strict";
-
-var fs = require('fs'),
-	yaml = require('js-yaml'),
-	_ = require('lodash');
-
-/**
- * Load Application Configuration
- *
- * @param      {String}  confPath  Path to the configuration file
- * @return     {Object}  Application Configuration
- */
-module.exports = (confPath) => {
-
-	var appconfig = {};
-
-	try {
-	  appconfig = yaml.safeLoad(fs.readFileSync(confPath, 'utf8'));
-	} catch (ex) {
-	  winston.error(ex);
-	  process.exit(1);
-	}
-
-	// Merge with defaults
-
-	appconfig = _.defaultsDeep(appconfig, {
-		title: "Requarks Wiki",
-		host: "http://localhost",
-		port: process.env.PORT,
-		auth: {
-			local: { enabled: true },
-			microsoft: { enabled: false },
-			google: { enabled: false },
-			facebook: { enabled: false },
-		},
-		db: "mongodb://localhost/wiki",
-		redis: null,
-		sessionSecret: null,
-		admin: null
-	});
-
-	// List authentication strategies
-	
-	appconfig.authStrategies = {
-		list: _.filter(appconfig.auth, ['enabled', true]),
-		socialEnabled: (_.chain(appconfig.auth).omit('local').reject({ enabled: false }).value().length > 0)
-	}
-	if(appconfig.authStrategies.list.length < 1) {
-		winston.error(new Error('You must enable at least 1 authentication strategy!'));
-	  process.exit(1);
-	}
-
-
-	return appconfig;
-
-};

+ 12 - 1
libs/markdown.js

@@ -151,7 +151,18 @@ const parseContent = (content)  => {
 
 	let output = mkdown.render(content);
 	let cr = cheerio.load(output);
-	cr('table').addClass('table is-bordered is-striped is-narrow');
+
+	//-> Style table headers
+
+	//cr('table').addClass('table is-bordered is-striped is-narrow');
+
+	//-> Remove links in headers
+
+	cr('h1 > a:not(.toc-anchor), h2 > a:not(.toc-anchor), h3 > a:not(.toc-anchor)').each((i, elm) => {
+		let txtLink = cr(elm).text();
+		cr(elm).replaceWith(txtLink);
+	});
+
 	output = cr.html();
 
 	return output;

+ 0 - 64
libs/mongo.js

@@ -1,64 +0,0 @@
-"use strict";
-
-const modb = require('mongoose'),
-			fs   = require("fs"),
-			path = require("path"),
-			_ = require('lodash');
-
-/**
- * MongoDB module
- *
- * @param      {Object}  appconfig  Application config
- * @return     {Object}  MongoDB wrapper instance
- */
-module.exports = {
-
-	/**
-	 * Initialize DB
-	 *
-	 * @param      {Object}  appconfig  The application config
-	 * @return     {Object}  DB instance
-	 */
-	init(appconfig) {
-
-		let self = this;
-
-		let dbModelsPath = path.resolve(ROOTPATH, 'models');
-
-		modb.Promise = require('bluebird');
-
-		// Event handlers
-
-		modb.connection.on('error', (err) => {
-			winston.error('[' + PROCNAME + '] Failed to connect to MongoDB instance.');
-		});
-		modb.connection.once('open', function() {
-			winston.log('[' + PROCNAME + '] Connected to MongoDB instance.');
-		});
-
-		// Store connection handle
-
-		self.connection = modb.connection;
-		self.ObjectId = modb.Types.ObjectId;
-
-		// Load DB Models
-
-		fs
-		.readdirSync(dbModelsPath)
-		.filter(function(file) {
-			return (file.indexOf(".") !== 0);
-		})
-		.forEach(function(file) {
-			let modelName = _.upperFirst(_.camelCase(_.split(file,'.')[0]));
-			self[modelName] = require(path.join(dbModelsPath, file));
-		});
-
-		// Connect
-
-		self.onReady = modb.connect(appconfig.db);
-
-		return self;
-
-	}
-
-};

+ 0 - 57
libs/rights.js

@@ -1,57 +0,0 @@
-"use strict";
-
-const _ = require('lodash');
-
-/**
- * Rights 
- */
-module.exports = {
-
-
-	check(req, role) {
-
-		let rt = [];
-		let p = _.chain(req.originalUrl).toLower().trim().value();
-
-		// Load User Rights
-
-		if(_.isArray(req.user.rights)) {
-			rt = req.user.rights;
-		}
-
-		// Is admin?
-
-		if(_.find(rt, { role: 'admin' })) {
-			return true;
-		}
-
-		// Check specific role on path
-
-		let filteredRights = _.filter(rt, (r) => {
-			if(r.role === role || (r.role === 'write' && role === 'read')) {
-				if((!r.exact && _.startsWith(p, r.path)) || (r.exact && p === r.path)) {
-					return true;
-				}
-			}
-			return false;
-		});
-
-		// Check for deny scenario
-
-		let isValid = false;
-
-		if(filteredRights.length > 1) {
-			isValid = !_.chain(filteredRights).sortBy((r) => {
-				return r.path.length + ((r.deny) ? 0.5 : 0);
-			}).last().get('deny').value();
-		} else if(filteredRights.length == 1 && filteredRights[0].deny === false) {
-			isValid = true;
-		}
-
-		// Deny by default
-
-		return isValid;
-
-	}
-
-};

+ 0 - 18
libs/winston.js

@@ -1,18 +0,0 @@
-"use strict";
-
-var winston = require('winston');
-
-module.exports = (isDebug) => {
-
-	winston.remove(winston.transports.Console);
-	winston.add(winston.transports.Console, {
-		level: (isDebug) ? 'debug' : 'info',
-		prettyPrint: true,
-		colorize: true,
-		silent: false,
-		timestamp: true
-	});
-
-	return winston;
-
-};

+ 2 - 4
models/bruteforce.js

@@ -1,13 +1,11 @@
 "use strict";
 
-const modb = require('mongoose');
-
 /**
  * BruteForce schema
  *
  * @type       {<Mongoose.Schema>}
  */
-var bruteForceSchema = modb.Schema({
+var bruteForceSchema = Mongoose.Schema({
 	_id: { type: String, index: 1 },
 	data: {
 		count: Number,
@@ -17,4 +15,4 @@ var bruteForceSchema = modb.Schema({
 	expires: { type: Date, index: { expires: '1d' } }
 });
 
-module.exports = modb.model('Bruteforce', bruteForceSchema);
+module.exports = Mongoose.model('Bruteforce', bruteForceSchema);

+ 3 - 4
models/entry.js

@@ -1,7 +1,6 @@
 "use strict";
 
-const modb = require('mongoose'),
-			Promise = require('bluebird'),
+const Promise = require('bluebird'),
 			_ = require('lodash');
 
 /**
@@ -9,7 +8,7 @@ const modb = require('mongoose'),
  *
  * @type       {<Mongoose.Schema>}
  */
-var entrySchema = modb.Schema({
+var entrySchema = Mongoose.Schema({
 
 	_id: String,
 
@@ -51,4 +50,4 @@ entrySchema.index({
   name: 'EntriesTextIndex'
 });
 
-module.exports = modb.model('Entry', entrySchema);
+module.exports = Mongoose.model('Entry', entrySchema);

+ 3 - 4
models/upl-file.js

@@ -1,7 +1,6 @@
 "use strict";
 
-const modb = require('mongoose'),
-			Promise = require('bluebird'),
+const Promise = require('bluebird'),
 			_ = require('lodash');
 
 /**
@@ -9,7 +8,7 @@ const modb = require('mongoose'),
  *
  * @type       {<Mongoose.Schema>}
  */
-var uplFileSchema = modb.Schema({
+var uplFileSchema = Mongoose.Schema({
 
 	_id: String,
 
@@ -48,4 +47,4 @@ var uplFileSchema = modb.Schema({
 	timestamps: {}
 });
 
-module.exports = modb.model('UplFile', uplFileSchema);
+module.exports = Mongoose.model('UplFile', uplFileSchema);

+ 3 - 4
models/upl-folder.js

@@ -1,7 +1,6 @@
 "use strict";
 
-const modb = require('mongoose'),
-			Promise = require('bluebird'),
+const Promise = require('bluebird'),
 			_ = require('lodash');
 
 /**
@@ -9,7 +8,7 @@ const modb = require('mongoose'),
  *
  * @type       {<Mongoose.Schema>}
  */
-var uplFolderSchema = modb.Schema({
+var uplFolderSchema = Mongoose.Schema({
 
 	_id: String,
 
@@ -23,4 +22,4 @@ var uplFolderSchema = modb.Schema({
 	timestamps: {}
 });
 
-module.exports = modb.model('UplFolder', uplFolderSchema);
+module.exports = Mongoose.model('UplFolder', uplFolderSchema);

+ 3 - 4
models/user.js

@@ -1,7 +1,6 @@
 "use strict";
 
-const modb = require('mongoose'),
-			Promise = require('bluebird'),
+const Promise = require('bluebird'),
 			bcrypt = require('bcryptjs-then'),
 			_ = require('lodash');
 
@@ -10,7 +9,7 @@ const modb = require('mongoose'),
  *
  * @type       {<Mongoose.Schema>}
  */
-var userSchema = modb.Schema({
+var userSchema = Mongoose.Schema({
 
 	email: {
 		type: String,
@@ -86,4 +85,4 @@ userSchema.methods.validatePassword = function(rawPwd) {
 	});
 };
 
-module.exports = modb.model('User', userSchema);
+module.exports = Mongoose.model('User', userSchema);

+ 3 - 0
package.json

@@ -81,7 +81,9 @@
     "pug": "^2.0.0-beta6",
     "read-chunk": "^2.0.0",
     "remove-markdown": "^0.1.0",
+    "requarks-core": "github:requarks/core",
     "request": "^2.75.0",
+    "search-index": "^0.9.6",
     "serve-favicon": "^2.3.0",
     "sharp": "^0.16.1",
     "simplemde": "^1.11.2",
@@ -111,6 +113,7 @@
     "gulp-sass": "^2.3.2",
     "gulp-tar": "^1.9.0",
     "gulp-uglify": "^2.0.0",
+    "gulp-watch": "^4.3.11",
     "gulp-zip": "^3.2.0",
     "istanbul": "^0.4.5",
     "jquery": "^3.1.1",

+ 12 - 10
server.js

@@ -5,30 +5,30 @@
 // Licensed under AGPLv3
 // ===========================================
 
-global.ROOTPATH = __dirname;
 global.PROCNAME = 'SERVER';
+global.ROOTPATH = __dirname;
+global.CORE_PATH = ROOTPATH + '/../core/';
+global.IS_DEBUG = process.env.NODE_ENV === 'development';
 
 // ----------------------------------------
 // Load Winston
 // ----------------------------------------
 
-const _isDebug = process.env.NODE_ENV === 'development';
-global.winston = require('./libs/winston')(_isDebug);
+global.winston = require(CORE_PATH + 'core-libs/winston')(IS_DEBUG);
 winston.info('[SERVER] Requarks Wiki is initializing...');
 
 // ----------------------------------------
 // Load global modules
 // ----------------------------------------
 
-var appconfig = require('./libs/config')('./config.yml');
+var appconfig = require(CORE_PATH + 'core-libs/config')('./config.yml');
 global.lcdata = require('./libs/local').init(appconfig);
-global.db = require('./libs/mongo').init(appconfig);
+global.db = require(CORE_PATH + 'core-libs/mongodb').init(appconfig);
 global.entries = require('./libs/entries').init(appconfig);
 global.git = require('./libs/git').init(appconfig, false);
 global.lang = require('i18next');
 global.mark = require('./libs/markdown');
 global.upl = require('./libs/uploads').init(appconfig);
-global.rights = require('./libs/rights');
 
 // ----------------------------------------
 // Load modules
@@ -53,7 +53,7 @@ const session = require('express-session');
 const sessionMongoStore = require('connect-mongo')(session);
 const socketio = require('socket.io');
 
-var mw = autoload(path.join(ROOTPATH, '/middlewares'));
+var mw = autoload(CORE_PATH + '/core-middlewares');
 var ctrl = autoload(path.join(ROOTPATH, '/controllers'));
 var libInternalAuth = require('./libs/internalAuth');
 
@@ -80,10 +80,12 @@ app.use(favicon(path.join(ROOTPATH, 'assets', 'favicon.ico')));
 app.use(express.static(path.join(ROOTPATH, 'assets')));
 
 // ----------------------------------------
-// Session
+// Passport Authentication
 // ----------------------------------------
 
-const strategies = require('./libs/auth')(passport, appconfig);
+var strategy = require(CORE_PATH + 'core-libs/auth')(passport, appconfig);
+global.rights = require(CORE_PATH + 'core-libs/rights');
+
 var sessionStore = new sessionMongoStore({
   mongooseConnection: db.connection,
   touchAfter: 15
@@ -165,7 +167,7 @@ app.use(function(err, req, res, next) {
   res.status(err.status || 500);
   res.render('error', {
     message: err.message,
-    error: _isDebug ? err : {}
+    error: IS_DEBUG ? err : {}
   });
 });
 

+ 4 - 4
views/common/header.pug

@@ -1,12 +1,12 @@
 
 #header-container
-	nav.nav.has-shadow.stickyscroll#header
+	nav.nav.stickyscroll#header
 		.nav-left
 			block rootNavLeft
-				a.nav-item.is-brand(href='/')
+				a.nav-item(href='/')
 					img(src='/favicons/android-icon-96x96.png', alt='Wiki')
 				a.nav-item(href='/')
-					h1.title Wiki
+					h1= appconfig.title
 		.nav-center
 			block rootNavCenter
 				.nav-item
@@ -16,7 +16,7 @@
 			span
 			span
 			span
-		.nav-right.nav-menu
+		.nav-right
 			block rootNavRight
 				i.nav-item#notifload
 

+ 1 - 1
views/layout.pug

@@ -28,7 +28,7 @@ html
 		block head
 
 	body
-		#root
+		#root.has-stickynav
 			include ./common/header.pug
 			include ./common/alerts.pug
 			main

+ 0 - 69
views/pages/account.pug

@@ -1,69 +0,0 @@
-extends ../layout.pug
-
-block rootNavCenter
-
-
-block rootNavRight
-	i.nav-item#notifload
-	span.nav-item
-		a.button.is-dark.is-outlined.btn-edit-discard(href='/')
-			span.icon
-				i.fa.fa-home
-			span Home
-
-block content
-
-	#page-type-account
-		section.hero.is-dark
-			.hero-body
-				.container
-					h1.title Account
-					h2.subtitle Manage your settings and site configuration
-		nav.nav.has-shadow
-			.container
-				.nav-left
-					a.nav-item.is-tab.is-active My Profile
-					a.nav-item.is-tab Stats
-					a.nav-item.is-tab Users
-					a.nav-item.is-tab Site Settings
-					a.nav-item.is-tab(href='/logout') Logout
-		section.section
-			.container
-				.columns
-					.column.is-two-thirds
-						label.label Email
-						p.control.has-icon.has-icon-right
-							input.input(type='email', placeholder='Email', value=user.email, disabled=(user.provider !== 'local'))
-							i.fa.fa-envelope
-						if user.provider == 'local'
-							label.label Password
-							p.control.has-icon.has-icon-right
-								input.input(type='password', placeholder='Password', value='********')
-								i.fa.fa-lock
-							label.label Verify Password
-							p.control.has-icon.has-icon-right
-								input.input(type='password', placeholder='Password', value='********')
-								i.fa.fa-lock
-						label.label Display Name
-						p.control.has-icon.has-icon-right
-							input.input(type='text', placeholder='John Smith', value=user.name)
-							i.fa.fa-user
-					.column
-						label.label Provider
-						p.control.account-profile-provider
-							case user.provider
-								when 'local': i.fa.fa-database
-								when 'windowslive': i.fa.fa-windows.is-blue
-								when 'google': i.fa.fa-google.is-blue
-								when 'facebook': i.fa.fa-facebook.is-purple
-								default: i.fa.fa-question-circle
-							= t('auth:providers.' + user.provider)
-						label.label Member since
-						p.control= userMoment(user.createdAt).format('LL')
-						label.label Last Profile Update
-						p.control= userMoment(user.updatedAt).format('LL')
-		section.section
-			.container
-				p.control
-					button.button.is-success
-						| Save Changes

+ 30 - 0
views/pages/admin/_layout.pug

@@ -0,0 +1,30 @@
+extends ../../layout.pug
+
+block rootNavCenter
+
+
+block rootNavRight
+	i.nav-item#notifload
+	span.nav-item
+		a.button.is-dark.is-outlined.btn-edit-discard(href='/')
+			span.icon
+				i.fa.fa-home
+			span Home
+
+block content
+
+	#page-type-account
+		section.hero.is-dark
+			.hero-body
+				.container
+					h1.title Account
+					h2.subtitle Manage your settings and site configuration
+		nav.nav.has-shadow
+			.container
+				.nav-left
+					a.nav-item.is-tab(href='/admin/profile', class={'is-active': adminTab === 'profile'}) My Profile
+					a.nav-item.is-tab(href='/admin/stats', class={'is-active': adminTab === 'stats'}) Stats
+					a.nav-item.is-tab(href='/admin/users', class={'is-active': adminTab === 'users'}) Users
+					a.nav-item.is-tab(href='/admin/settings', class={'is-active': adminTab === 'settings'}) Site Settings
+					a.nav-item.is-tab(href='/logout') Logout
+		block adminContent

+ 43 - 0
views/pages/admin/profile.pug

@@ -0,0 +1,43 @@
+extends ./_layout.pug
+
+block adminContent
+	section.section
+		.container
+			.columns
+				.column.is-two-thirds
+					label.label Email
+					p.control.has-icon.has-icon-right
+						input.input(type='email', placeholder='Email', value=user.email, disabled=(user.provider !== 'local'))
+						i.fa.fa-envelope
+					if user.provider == 'local'
+						label.label Password
+						p.control.has-icon.has-icon-right
+							input.input(type='password', placeholder='Password', value='********')
+							i.fa.fa-lock
+						label.label Verify Password
+						p.control.has-icon.has-icon-right
+							input.input(type='password', placeholder='Password', value='********')
+							i.fa.fa-lock
+					label.label Display Name
+					p.control.has-icon.has-icon-right
+						input.input(type='text', placeholder='John Smith', value=user.name)
+						i.fa.fa-user
+				.column
+					label.label Provider
+					p.control.account-profile-provider
+						case user.provider
+							when 'local': i.fa.fa-database
+							when 'windowslive': i.fa.fa-windows.is-blue
+							when 'google': i.fa.fa-google.is-blue
+							when 'facebook': i.fa.fa-facebook.is-purple
+							default: i.fa.fa-question-circle
+						= t('auth:providers.' + user.provider)
+					label.label Member since
+					p.control= userMoment(user.createdAt).format('LL')
+					label.label Last Profile Update
+					p.control= userMoment(user.updatedAt).format('LL')
+	section.section
+		.container
+			p.control
+				button.button.is-success
+					| Save Changes

+ 6 - 0
views/pages/admin/settings.pug

@@ -0,0 +1,6 @@
+extends ./_layout.pug
+
+block adminContent
+	section.section
+		.container
+			label.label Coming soon

+ 11 - 0
views/pages/admin/stats.pug

@@ -0,0 +1,11 @@
+extends ./_layout.pug
+
+block adminContent
+	section.section
+		.container
+			label.label Entries
+			p.control= totalEntries
+			label.label Uploads
+			p.control= totalUploads
+			label.label Users
+			p.control= totalUsers

+ 15 - 0
views/pages/admin/users.pug

@@ -0,0 +1,15 @@
+extends ./_layout.pug
+
+block adminContent
+	section.section
+		.container
+			.columns
+				.column.is-three-quarters
+					label.label Coming soon
+				.column
+					p.control
+						button.button.is-info
+							| Create Local User
+					p.control
+						button.button.is-info
+							| Authorize Social User

+ 4 - 6
views/pages/edit.pug

@@ -6,13 +6,11 @@ block rootNavCenter
 block rootNavRight
 	i.nav-item#notifload
 	span.nav-item
-		a.button.is-warning.is-outlined.btn-edit-discard
-			span.icon
-				i.fa.fa-times
+		a.button.is-orange.is-outlined.btn-edit-discard
+			i.fa.fa-times
 			span Discard
-		a.button.is-success.btn-edit-save
-			span.icon
-				i.fa.fa-check
+		a.button.is-green.btn-edit-save
+			i.fa.fa-check
 			span Save Changes
 
 block content

+ 4 - 6
views/pages/source.pug

@@ -10,13 +10,11 @@ block rootNavRight
 	a.nav-item(href='/' + pageData.meta.path)
 		| Normal View
 	span.nav-item
-		a.button(href='/edit/' + pageData.meta.path)
-			span.icon
-				i.fa.fa-edit
+		a.button.is-orange(href='/edit/' + pageData.meta.path)
+			i.fa.fa-edit
 			span Edit
-		a.button.is-primary.btn-create-prompt
-			span.icon
-				i.fa.fa-plus
+		a.button.is-blue.btn-create-prompt
+			i.fa.fa-plus
 			span Create
 
 block content

+ 36 - 32
views/pages/view.pug

@@ -15,51 +15,55 @@ block rootNavRight
 	a.nav-item(href='/source/' + pageData.meta.path)
 		| Source
 	span.nav-item
-		a.button(href='/edit/' + pageData.meta.path)
-			span.icon
-				i.fa.fa-edit
+		a.button.is-orange.is-outlined(href='/edit/' + pageData.meta.path)
+			i.fa.fa-edit
 			span Edit
-		a.button.is-primary.btn-create-prompt
-			span.icon
-				i.fa.fa-plus
+		a.button.is-light-blue.btn-create-prompt
+			i.fa.fa-plus
 			span Create
 
 block content
 
 	#page-type-view(data-entrypath=pageData.meta.path)
-		section.section
-			.container.is-fluid
-				.columns
+		.container.is-fluid
+			.columns.is-gapless
 
-					.column.is-narrow.sd-menus.is-hidden-touch
+				.column.is-narrow.is-hidden-touch.sidebar
 
-						.box
-							aside.menu(style= { 'min-width': '200px' })
-								p.menu-label
-									| Navigation
-								ul.menu-list
-									li
-										a(href='/') Home
-									if pageData.parent
-										li
-											a(href='/' + pageData.parent.path)= pageData.parent.title
-									li
-										a(href='/admin') Account
-						.box.stickyscroll(data-margin-top=70)
-							aside.menu(style= { 'min-width': '200px' })
-								p.menu-label
-									| Contents
-								ul.menu-list
-									a(href='#root', title='Start') Start
-									+tocMenu(pageData.tree)
+					aside
+						.sidebar-label
+							i.icon-th-small
+							span Navigation
+						ul.sidebar-menu
+							li
+								a(href='/')
+									i.icon-home
+									span Home
+							if pageData.parent
+								li
+									a(href='/' + pageData.parent.path)
+										i.icon-reply
+										span= pageData.parent.title
+							li
+								a(href='/admin')
+									i.icon-head
+									span Account
+					aside.stickyscroll(data-margin-top=40)
+						.sidebar-label
+							i.icon-th-list
+							span Contents
+						ul.sidebar-menu
+							li: a(href='#root', title='Start') Start
+							+tocMenu(pageData.tree)
 
-					.column
+				.column
 
+					.hero
 						h1.title#title= pageData.meta.title
 						if pageData.meta.subtitle
 							h2.subtitle= pageData.meta.subtitle
-						.content.mkcontent
-							!= pageData.html
+					.content.mkcontent
+						!= pageData.html
 
 	include ../modals/create.pug
 	include ../modals/move.pug

部分文件因文件數量過多而無法顯示