├── slick-postgresql-redis ├── ps-web │ ├── static │ │ ├── README │ │ ├── src │ │ │ └── site │ │ │ │ ├── scss │ │ │ │ ├── static.scss │ │ │ │ └── bootstrap │ │ │ │ │ ├── mixins │ │ │ │ │ ├── _center-block.scss │ │ │ │ │ ├── _opacity.scss │ │ │ │ │ ├── _size.scss │ │ │ │ │ ├── _text-overflow.scss │ │ │ │ │ ├── _tab-focus.scss │ │ │ │ │ ├── _labels.scss │ │ │ │ │ ├── _resize.scss │ │ │ │ │ ├── _text-emphasis.scss │ │ │ │ │ ├── _progress-bar.scss │ │ │ │ │ ├── _background-variant.scss │ │ │ │ │ ├── _reset-filter.scss │ │ │ │ │ ├── _nav-divider.scss │ │ │ │ │ ├── _alerts.scss │ │ │ │ │ ├── _nav-vertical-align.scss │ │ │ │ │ ├── _pagination.scss │ │ │ │ │ ├── _border-radius.scss │ │ │ │ │ ├── _responsive-visibility.scss │ │ │ │ │ ├── _panels.scss │ │ │ │ │ ├── _hide-text.scss │ │ │ │ │ ├── _clearfix.scss │ │ │ │ │ ├── _list-group.scss │ │ │ │ │ ├── _table-row.scss │ │ │ │ │ ├── _buttons.scss │ │ │ │ │ ├── _image.scss │ │ │ │ │ ├── _grid-framework.scss │ │ │ │ │ ├── _forms.scss │ │ │ │ │ ├── _grid.scss │ │ │ │ │ ├── _gradients.scss │ │ │ │ │ └── _vendor-prefixes.scss │ │ │ │ │ ├── _wells.scss │ │ │ │ │ ├── _breadcrumbs.scss │ │ │ │ │ ├── _responsive-embed.scss │ │ │ │ │ ├── _close.scss │ │ │ │ │ ├── _component-animations.scss │ │ │ │ │ ├── _utilities.scss │ │ │ │ │ ├── _media.scss │ │ │ │ │ ├── _thumbnails.scss │ │ │ │ │ ├── _messager.scss │ │ │ │ │ ├── _pager.scss │ │ │ │ │ ├── _mixins.scss │ │ │ │ │ ├── _jumbotron.scss │ │ │ │ │ ├── bootstrap.scss │ │ │ │ │ ├── _labels.scss │ │ │ │ │ ├── _badges.scss │ │ │ │ │ ├── _code.scss │ │ │ │ │ ├── _grid.scss │ │ │ │ │ ├── _alerts.scss │ │ │ │ │ ├── _progress-bars.scss │ │ │ │ │ ├── _pagination.scss │ │ │ │ │ ├── _print.scss │ │ │ │ │ ├── _tooltip.scss │ │ │ │ │ ├── _list-group.scss │ │ │ │ │ ├── _scaffolding.scss │ │ │ │ │ ├── _popovers.scss │ │ │ │ │ ├── _modals.scss │ │ │ │ │ ├── _buttons.scss │ │ │ │ │ ├── _input-groups.scss │ │ │ │ │ ├── _responsive-utilities.scss │ │ │ │ │ ├── _tables.scss │ │ │ │ │ ├── _dropdowns.scss │ │ │ │ │ ├── _navs.scss │ │ │ │ │ ├── _carousel.scss │ │ │ │ │ ├── _button-groups.scss │ │ │ │ │ ├── _panels.scss │ │ │ │ │ └── _type.scss │ │ │ │ ├── img │ │ │ │ └── favicon.jpg │ │ │ │ └── js │ │ │ │ └── sign.js │ │ ├── package.json │ │ └── gulpfile.js │ ├── app │ │ ├── views │ │ │ ├── home │ │ │ │ └── main.scala.html │ │ │ ├── account │ │ │ │ ├── signin.scala.html │ │ │ │ └── signup.scala.html │ │ │ └── _layout.scala.html │ │ ├── controllers │ │ │ ├── BaseController.scala │ │ │ ├── UserAction.scala │ │ │ ├── PageController.scala │ │ │ ├── inapi │ │ │ │ └── UserController.scala │ │ │ └── WebTools.scala │ │ └── utils │ │ │ ├── ErrorHandler.scala │ │ │ └── Filters.scala │ └── conf │ │ ├── application.conf │ │ ├── routes │ │ └── logback.xml ├── project │ ├── build.properties │ ├── plugins.sbt │ ├── BuildSettings.scala │ ├── Build.scala │ └── Dependencies.scala ├── ps-data │ └── src │ │ ├── main │ │ ├── scala │ │ │ └── me │ │ │ │ └── yangbajing │ │ │ │ └── ps │ │ │ │ └── data │ │ │ │ ├── domain │ │ │ │ ├── RegisterParam.scala │ │ │ │ ├── OwnerToken.scala │ │ │ │ └── CaseClass.scala │ │ │ │ ├── implicits │ │ │ │ ├── RecordJsonImplicits.scala │ │ │ │ └── JsonImplicits.scala │ │ │ │ ├── record │ │ │ │ ├── Credential.scala │ │ │ │ ├── User.scala │ │ │ │ ├── DatabaseModule.scala │ │ │ │ └── Schemas.scala │ │ │ │ ├── PsSqlDriver.scala │ │ │ │ └── repo │ │ │ │ └── UserRepo.scala │ │ └── resources │ │ │ └── reference.conf │ │ └── test │ │ └── scala │ │ └── me │ │ └── yangbajing │ │ └── ps │ │ └── data │ │ ├── domain │ │ └── CaseClassTest.scala │ │ └── record │ │ └── SchemasTest.scala ├── .gitignore ├── ps-util │ └── src │ │ ├── test │ │ └── scala │ │ │ └── me │ │ │ └── yangbajing │ │ │ ├── UnitSpec.scala │ │ │ └── UnitPlaySpec.scala │ │ └── main │ │ ├── scala │ │ └── me │ │ │ └── yangbajing │ │ │ └── ps │ │ │ ├── util │ │ │ ├── Constants.scala │ │ │ ├── RandomUtils.scala │ │ │ ├── Utils.scala │ │ │ └── enums │ │ │ │ └── JsValueEnumUtils.scala │ │ │ ├── types │ │ │ └── OwnerStatus.scala │ │ │ └── setting │ │ │ └── SettingComponent.scala │ │ └── resources │ │ └── reference.conf ├── ps-business │ └── src │ │ └── main │ │ └── scala │ │ └── me │ │ └── yangbajing │ │ └── ps │ │ └── business │ │ └── service │ │ ├── CacheService.scala │ │ └── UserService.scala └── README.md ├── README.md └── .gitignore /slick-postgresql-redis/ps-web/static/README: -------------------------------------------------------------------------------- 1 | # play-seed 前端代码 -------------------------------------------------------------------------------- /slick-postgresql-redis/project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.0.0 2 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/static.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap/bootstrap"; 2 | 3 | .sign-panel { 4 | margin-top: 100px; 5 | padding-top: 15px; 6 | } -------------------------------------------------------------------------------- /slick-postgresql-redis/project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.typesafe.play" %% "sbt-plugin" % "2.5.4") 2 | 3 | addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2") -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/img/favicon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yangjing/play-seed/HEAD/slick-postgresql-redis/ps-web/static/src/site/img/favicon.jpg -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/main/scala/me/yangbajing/ps/data/domain/RegisterParam.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.data.domain 2 | 3 | case class RegisterParam(email: Option[String], phone: Option[String], password: String) 4 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/app/views/home/main.scala.html: -------------------------------------------------------------------------------- 1 | @(user: me.yangbajing.ps.data.domain.OwnerToken) 2 | @views.html._layout("用户主页") { 3 |
4 |

@user.toString

5 |
6 | } -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_center-block.scss: -------------------------------------------------------------------------------- 1 | // Center-align a block level element 2 | 3 | @mixin center-block() { 4 | display: block; 5 | margin-left: auto; 6 | margin-right: auto; 7 | } 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # play-seed 2 | 3 | - 作者:杨景(羊八井) 4 | - 邮箱:yangbajing@gmail.com 5 | - 主页:http://www.yangbajing.me 6 | - 微博:http://weibo.com/yangbajing 7 | 8 | 9 | ## 示例工程 10 | 11 | - [slick-postgresql-redis](slick-postgresql-redis) 12 | 13 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_opacity.scss: -------------------------------------------------------------------------------- 1 | // Opacity 2 | 3 | @mixin opacity($opacity) { 4 | opacity: $opacity; 5 | // IE8 filter 6 | $opacity-ie: ($opacity * 100); 7 | filter: alpha(opacity=$opacity-ie); 8 | } 9 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_size.scss: -------------------------------------------------------------------------------- 1 | // Sizing shortcuts 2 | 3 | @mixin size($width, $height) { 4 | width: $width; 5 | height: $height; 6 | } 7 | 8 | @mixin square($size) { 9 | @include size($size, $size); 10 | } 11 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/conf/application.conf: -------------------------------------------------------------------------------- 1 | play.crypto.secret = "QY#q!^N|3]HzkzVdHouAeUE=cEZb+k`Hy;]N1`3nHLv'vhF=2J-K>zq-+V13./5m" 2 | play { 3 | http { 4 | errorHandler = "utils.ErrorHandler" 5 | filters = "utils.Filters" 6 | server.port = 58082 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_text-overflow.scss: -------------------------------------------------------------------------------- 1 | // Text overflow 2 | // Requires inline-block or block for proper styling 3 | 4 | @mixin text-overflow() { 5 | overflow: hidden; 6 | text-overflow: ellipsis; 7 | white-space: nowrap; 8 | } 9 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_tab-focus.scss: -------------------------------------------------------------------------------- 1 | // WebKit-style focus 2 | 3 | @mixin tab-focus() { 4 | // Default 5 | outline: thin dotted; 6 | // WebKit 7 | outline: 5px auto -webkit-focus-ring-color; 8 | outline-offset: -2px; 9 | } 10 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_labels.scss: -------------------------------------------------------------------------------- 1 | // Labels 2 | 3 | @mixin label-variant($color) { 4 | background-color: $color; 5 | 6 | &[href] { 7 | &:hover, 8 | &:focus { 9 | background-color: darken($color, 10%); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_resize.scss: -------------------------------------------------------------------------------- 1 | // Resize anything 2 | 3 | @mixin resizable($direction) { 4 | resize: $direction; // Options: horizontal, vertical, both 5 | overflow: auto; // Per CSS3 UI, `resize` only applies when `overflow` isn't `visible` 6 | } 7 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_text-emphasis.scss: -------------------------------------------------------------------------------- 1 | // Typography 2 | 3 | // [converter] $parent hack 4 | @mixin text-emphasis-variant($parent, $color) { 5 | #{$parent} { 6 | color: $color; 7 | } 8 | a#{$parent}:hover { 9 | color: darken($color, 10%); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_progress-bar.scss: -------------------------------------------------------------------------------- 1 | // Progress bars 2 | 3 | @mixin progress-bar-variant($color) { 4 | background-color: $color; 5 | 6 | // Deprecated parent class requirement as of v3.2.0 7 | .progress-striped & { 8 | @include gradient-striped; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_background-variant.scss: -------------------------------------------------------------------------------- 1 | // Contextual backgrounds 2 | 3 | // [converter] $parent hack 4 | @mixin bg-variant($parent, $color) { 5 | #{$parent} { 6 | background-color: $color; 7 | } 8 | a#{$parent}:hover { 9 | background-color: darken($color, 10%); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_reset-filter.scss: -------------------------------------------------------------------------------- 1 | // Reset filters for IE 2 | // 3 | // When you need to remove a gradient background, do not forget to use this to reset 4 | // the IE filter for IE9 and below. 5 | 6 | @mixin reset-filter() { 7 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 8 | } 9 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_nav-divider.scss: -------------------------------------------------------------------------------- 1 | // Horizontal dividers 2 | // 3 | // Dividers (basically an hr) within dropdowns and nav lists 4 | 5 | @mixin nav-divider($color: #e5e5e5) { 6 | height: 1px; 7 | margin: (($line-height-computed / 2) - 1) 0; 8 | overflow: hidden; 9 | background-color: $color; 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | .travis.yml 4 | 5 | node_modules/ 6 | 7 | # Ignore Play! working directory # 8 | bin/ 9 | /db 10 | /public/ 11 | .eclipse 12 | /lib/ 13 | /logs/ 14 | /modules 15 | /project/project 16 | /project/target 17 | target/ 18 | tmp/ 19 | test-result 20 | server.pid 21 | *.iml 22 | *.eml 23 | /dist/ 24 | .cache 25 | /ps-web/logs/ 26 | /ps-web/public/ 27 | -------------------------------------------------------------------------------- /slick-postgresql-redis/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | .travis.yml 4 | 5 | node_modules/ 6 | 7 | # Ignore Play! working directory # 8 | bin/ 9 | /db 10 | /public/ 11 | .eclipse 12 | /lib/ 13 | /logs/ 14 | /modules 15 | /project/project 16 | /project/target 17 | target/ 18 | tmp/ 19 | test-result 20 | server.pid 21 | *.iml 22 | *.eml 23 | /dist/ 24 | .cache 25 | /ps-web/logs/ 26 | /ps-web/public/ 27 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_alerts.scss: -------------------------------------------------------------------------------- 1 | // Alerts 2 | 3 | @mixin alert-variant($background, $border, $text-color) { 4 | background-color: $background; 5 | border-color: $border; 6 | color: $text-color; 7 | 8 | hr { 9 | border-top-color: darken($border, 5%); 10 | } 11 | .alert-link { 12 | color: darken($text-color, 10%); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/app/controllers/BaseController.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import com.typesafe.scalalogging.LazyLogging 4 | import me.yangbajing.ps.data.implicits.JsonImplicits 5 | import play.api.libs.concurrent.Execution 6 | import play.api.mvc.Controller 7 | 8 | trait BaseController extends Controller with JsonImplicits with LazyLogging { 9 | this: Controller => 10 | 11 | implicit def __ec = Execution.Implicits.defaultContext 12 | } 13 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_nav-vertical-align.scss: -------------------------------------------------------------------------------- 1 | // Navbar vertical align 2 | // 3 | // Vertically center elements in the navbar. 4 | // Example: an element has a height of 30px, so write out `.navbar-vertical-align(30px);` to calculate the appropriate top margin. 5 | 6 | @mixin navbar-vertical-align($element-height) { 7 | margin-top: (($navbar-height - $element-height) / 2); 8 | margin-bottom: (($navbar-height - $element-height) / 2); 9 | } 10 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/main/scala/me/yangbajing/ps/data/implicits/RecordJsonImplicits.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.data.implicits 2 | 3 | import me.yangbajing.ps.data.record.User 4 | import play.api.libs.json.{Json, Format} 5 | 6 | /** 7 | * play json format 8 | * Created by jingyang on 15/7/16. 9 | */ 10 | trait RecordJsonImplicits { 11 | implicit def __userFormat: Format[User] = Json.format[User] 12 | } 13 | 14 | object RecordJsonImplicits extends RecordJsonImplicits 15 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/main/resources/reference.conf: -------------------------------------------------------------------------------- 1 | slick.dbs { 2 | default { 3 | driver = "me.yangbajing.ps.data.PsSqlDriver$" 4 | db { 5 | dataSourceClass = "org.postgresql.ds.PGSimpleDataSource" 6 | driver = "org.postgresql.Driver" 7 | properties = { 8 | serverName = "localhost" 9 | databaseName = "play_seed" 10 | user = "playseeduser" 11 | password = "playseedpass" 12 | } 13 | numThreads = 10 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-util/src/test/scala/me/yangbajing/UnitSpec.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing 2 | 3 | import org.scalatest._ 4 | import org.scalatest.concurrent.ScalaFutures 5 | import org.scalatest.time.{Millis, Seconds, Span} 6 | 7 | trait UnitSpec extends WordSpec with MustMatchers with OptionValues with ScalaFutures { 8 | 9 | override implicit def patienceConfig: PatienceConfig = 10 | PatienceConfig(timeout = scaled(Span(90, Seconds)), interval = scaled(Span(100, Millis))) 11 | 12 | } 13 | 14 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/conf/routes: -------------------------------------------------------------------------------- 1 | GET /assets/*file @controllers.Assets.at(path = "/public", file) 2 | GET /account/signin @controllers.PageController.signin() 3 | GET /account/signup @controllers.PageController.signup() 4 | GET /home @controllers.PageController.homeMain() 5 | POST /inapi/user/signup @controllers.inapi.UserController.signup() 6 | POST /inapi/user/signin @controllers.inapi.UserController.signin() 7 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-util/src/main/scala/me/yangbajing/ps/util/Constants.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.util 2 | 3 | import java.nio.charset.Charset 4 | 5 | /** 6 | * 常量 7 | * Created by jingyang on 15/7/16. 8 | */ 9 | object Constants { 10 | val COOKIE_MAX_AGE = 30 // 秒 11 | 12 | val CACHE_KEY_TOKEN_PREFIX = "ps:to:" 13 | 14 | val NICK = "nick" 15 | 16 | val SPLIT_CHAR = '-' 17 | 18 | val SALT_LENGTH = 12 19 | 20 | val CHARSET = Charset.forName("UTF-8") 21 | 22 | val PS_TOKEN_OWNER = "ps-to" 23 | } 24 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/main/scala/me/yangbajing/ps/data/record/Credential.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.data.record 2 | 3 | import java.time.LocalDateTime 4 | 5 | /** 6 | * 保存账号、密码等敏感信息 7 | * Created by jingyang on 15/7/16. 8 | */ 9 | case class Credential(id: Option[Long], 10 | email: Option[String], 11 | phone: Option[String], 12 | salt: Array[Byte], 13 | password: Array[Byte], 14 | created_at: LocalDateTime) 15 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/main/scala/me/yangbajing/ps/data/implicits/JsonImplicits.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.data.implicits 2 | 3 | import me.yangbajing.ps.data.domain.{RegisterParam, OwnerToken} 4 | import play.api.libs.json.{Format, Json} 5 | 6 | trait JsonImplicits extends RecordJsonImplicits { 7 | implicit def __registerParamFormats: Format[RegisterParam] = Json.format[RegisterParam] 8 | implicit def __userTokenFormats: Format[OwnerToken] = Json.format[OwnerToken] 9 | } 10 | 11 | object JsonImplicits extends JsonImplicits 12 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-util/src/main/scala/me/yangbajing/ps/types/OwnerStatus.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.types 2 | 3 | import me.yangbajing.ps.util.enums.JsValueEnumUtils 4 | import play.api.libs.json.Format 5 | 6 | /** 7 | * 用户状态 8 | * Created by jingyang on 15/7/16. 9 | */ 10 | object OwnerStatus extends Enumeration { 11 | type OwnerStatus = Value 12 | 13 | val ACTIVE = Value("A") 14 | val INACTIVE = Value("I") 15 | val DELETE = Value("D") 16 | 17 | implicit def __ownerStatusFormats: Format[OwnerStatus] = JsValueEnumUtils.enumFormat(OwnerStatus) 18 | } 19 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-util/src/test/scala/me/yangbajing/UnitPlaySpec.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing 2 | 3 | import org.scalatest.concurrent.ScalaFutures 4 | import org.scalatest.time.{Millis, Seconds, Span} 5 | import org.scalatestplus.play.PlaySpec 6 | 7 | /** 8 | * Created by Yang Jing (yangbajing@gmail.com) on 2016-07-27. 9 | */ 10 | trait UnitPlaySpec extends PlaySpec with ScalaFutures { 11 | 12 | override implicit def patienceConfig: PatienceConfig = 13 | PatienceConfig(timeout = scaled(Span(90, Seconds)), interval = scaled(Span(100, Millis))) 14 | 15 | } 16 | 17 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_pagination.scss: -------------------------------------------------------------------------------- 1 | // Pagination 2 | 3 | @mixin pagination-size($padding-vertical, $padding-horizontal, $font-size, $border-radius) { 4 | > li { 5 | > a, 6 | > span { 7 | padding: $padding-vertical $padding-horizontal; 8 | font-size: $font-size; 9 | } 10 | &:first-child { 11 | > a, 12 | > span { 13 | @include border-left-radius($border-radius); 14 | } 15 | } 16 | &:last-child { 17 | > a, 18 | > span { 19 | @include border-right-radius($border-radius); 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_border-radius.scss: -------------------------------------------------------------------------------- 1 | // Single side border-radius 2 | 3 | @mixin border-top-radius($radius) { 4 | border-top-right-radius: $radius; 5 | border-top-left-radius: $radius; 6 | } 7 | @mixin border-right-radius($radius) { 8 | border-bottom-right-radius: $radius; 9 | border-top-right-radius: $radius; 10 | } 11 | @mixin border-bottom-radius($radius) { 12 | border-bottom-right-radius: $radius; 13 | border-bottom-left-radius: $radius; 14 | } 15 | @mixin border-left-radius($radius) { 16 | border-bottom-left-radius: $radius; 17 | border-top-left-radius: $radius; 18 | } 19 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_responsive-visibility.scss: -------------------------------------------------------------------------------- 1 | // Responsive utilities 2 | 3 | // 4 | // More easily include all the states for responsive-utilities.less. 5 | // [converter] $parent hack 6 | @mixin responsive-visibility($parent) { 7 | #{$parent} { 8 | display: block !important; 9 | } 10 | table#{$parent} { display: table; } 11 | tr#{$parent} { display: table-row !important; } 12 | th#{$parent}, 13 | td#{$parent} { display: table-cell !important; } 14 | } 15 | 16 | // [converter] $parent hack 17 | @mixin responsive-invisibility($parent) { 18 | #{$parent} { 19 | display: none !important; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/main/scala/me/yangbajing/ps/data/domain/OwnerToken.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.data.domain 2 | 3 | import me.yangbajing.ps.data.implicits.JsonImplicits 4 | import me.yangbajing.ps.util.Constants 5 | import play.api.libs.json.Json 6 | 7 | case class OwnerToken(ownerId: Long, email: Option[String], phone: Option[String], nick: Option[String]) { 8 | def toValue: String = ownerId.toString + Constants.SPLIT_CHAR + 9 | email.getOrElse("") + Constants.SPLIT_CHAR + 10 | phone.getOrElse("") + Constants.SPLIT_CHAR + 11 | nick.getOrElse("") + Constants.SPLIT_CHAR 12 | 13 | override def toString: String = Json.stringify(Json.toJson(this)(JsonImplicits.__userTokenFormats)) 14 | } 15 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_panels.scss: -------------------------------------------------------------------------------- 1 | // Panels 2 | 3 | @mixin panel-variant($border, $heading-text-color, $heading-bg-color, $heading-border) { 4 | border-color: $border; 5 | 6 | & > .panel-heading { 7 | color: $heading-text-color; 8 | background-color: $heading-bg-color; 9 | border-color: $heading-border; 10 | 11 | + .panel-collapse > .panel-body { 12 | border-top-color: $border; 13 | } 14 | .badge { 15 | color: $heading-bg-color; 16 | background-color: $heading-text-color; 17 | } 18 | } 19 | & > .panel-footer { 20 | + .panel-collapse > .panel-body { 21 | border-bottom-color: $border; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/main/scala/me/yangbajing/ps/data/record/User.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.data.record 2 | 3 | import java.time.LocalDateTime 4 | 5 | import me.yangbajing.ps.data.implicits.RecordJsonImplicits 6 | import me.yangbajing.ps.types.OwnerStatus._ 7 | import play.api.libs.json.{JsValue, Json} 8 | 9 | /** 10 | * 用户 11 | * Created by jingyang on 15/7/16. 12 | */ 13 | case class User(id: Long, 14 | attrs: JsValue, 15 | status: OwnerStatus, 16 | created_at: LocalDateTime) { 17 | def nick = attrs.\("nick").asOpt[String].getOrElse("") 18 | 19 | override def toString: String = Json.stringify(Json.toJson(this)(RecordJsonImplicits.__userFormat)) 20 | } 21 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_wells.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Wells 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .well { 8 | min-height: 20px; 9 | padding: 19px; 10 | margin-bottom: 20px; 11 | background-color: $well-bg; 12 | border: 1px solid $well-border; 13 | border-radius: $border-radius-base; 14 | @include box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 15 | blockquote { 16 | border-color: #ddd; 17 | border-color: rgba(0,0,0,.15); 18 | } 19 | } 20 | 21 | // Sizes 22 | .well-lg { 23 | padding: 24px; 24 | border-radius: $border-radius-large; 25 | } 26 | .well-sm { 27 | padding: 9px; 28 | border-radius: $border-radius-small; 29 | } 30 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/test/scala/me/yangbajing/ps/data/domain/CaseClassTest.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.data.domain 2 | 3 | import me.yangbajing.UnitSpec 4 | import play.api.libs.json.Json 5 | 6 | /** 7 | * Created by Yang Jing (yangbajing@gmail.com) on 2016-01-04. 8 | */ 9 | class CaseClassTest extends UnitSpec { 10 | "CaseClassTest" should { 11 | val cc = CaseClass("哈哈") 12 | 13 | "json marshal" in { 14 | val jv = Json.toJson(cc) 15 | jv.\("a").as[String] mustBe cc.a 16 | } 17 | 18 | "json unmarshal" in { 19 | val s = """{"b":"BB","c":"CC}""" 20 | val jv = Json.parse(s) 21 | jv.\("a").asOpt[String] mustBe None 22 | jv.\("c").as[String] mustBe "CC" 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_hide-text.scss: -------------------------------------------------------------------------------- 1 | // CSS image replacement 2 | // 3 | // Heads up! v3 launched with with only `.hide-text()`, but per our pattern for 4 | // mixins being reused as classes with the same name, this doesn't hold up. As 5 | // of v3.0.1 we have added `.text-hide()` and deprecated `.hide-text()`. 6 | // 7 | // Source: https://github.com/h5bp/html5-boilerplate/commit/aa0396eae757 8 | 9 | // Deprecated as of v3.0.1 (will be removed in v4) 10 | @mixin hide-text() { 11 | font: 0/0 a; 12 | color: transparent; 13 | text-shadow: none; 14 | background-color: transparent; 15 | border: 0; 16 | } 17 | 18 | // New mixin to use as of v3.0.1 19 | @mixin text-hide() { 20 | @include hide-text; 21 | } 22 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_breadcrumbs.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: $breadcrumb-padding-vertical $breadcrumb-padding-horizontal; 8 | margin-bottom: $line-height-computed; 9 | list-style: none; 10 | background-color: $breadcrumb-bg; 11 | border-radius: $border-radius-base; 12 | 13 | > li { 14 | display: inline-block; 15 | 16 | + li:before { 17 | content: "#{$breadcrumb-separator}\00a0"; // Unicode space added since inline-block means non-collapsing white-space 18 | padding: 0 5px; 19 | color: $breadcrumb-color; 20 | } 21 | } 22 | 23 | > .active { 24 | color: $breadcrumb-active-color; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_responsive-embed.scss: -------------------------------------------------------------------------------- 1 | // Embeds responsive 2 | // 3 | // Credit: Nicolas Gallagher and SUIT CSS. 4 | 5 | .embed-responsive { 6 | position: relative; 7 | display: block; 8 | height: 0; 9 | padding: 0; 10 | overflow: hidden; 11 | 12 | .embed-responsive-item, 13 | iframe, 14 | embed, 15 | object, 16 | video { 17 | position: absolute; 18 | top: 0; 19 | left: 0; 20 | bottom: 0; 21 | height: 100%; 22 | width: 100%; 23 | border: 0; 24 | } 25 | } 26 | 27 | // Modifier class for 16:9 aspect ratio 28 | .embed-responsive-16by9 { 29 | padding-bottom: 56.25%; 30 | } 31 | 32 | // Modifier class for 4:3 aspect ratio 33 | .embed-responsive-4by3 { 34 | padding-bottom: 75%; 35 | } 36 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_clearfix.scss: -------------------------------------------------------------------------------- 1 | // Clearfix 2 | // 3 | // For modern browsers 4 | // 1. The space content is one way to avoid an Opera bug when the 5 | // contenteditable attribute is included anywhere else in the document. 6 | // Otherwise it causes space to appear at the top and bottom of elements 7 | // that are clearfixed. 8 | // 2. The use of `table` rather than `block` is only necessary if using 9 | // `:before` to contain the top-margins of child elements. 10 | // 11 | // Source: http://nicolasgallagher.com/micro-clearfix-hack/ 12 | 13 | @mixin clearfix() { 14 | &:before, 15 | &:after { 16 | content: " "; // 1 17 | display: table; // 2 18 | } 19 | &:after { 20 | clear: both; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_list-group.scss: -------------------------------------------------------------------------------- 1 | // List Groups 2 | 3 | @mixin list-group-item-variant($state, $background, $color) { 4 | .list-group-item-#{$state} { 5 | color: $color; 6 | background-color: $background; 7 | 8 | // [converter] extracted a& to a.list-group-item-#{$state} 9 | } 10 | 11 | a.list-group-item-#{$state} { 12 | color: $color; 13 | 14 | .list-group-item-heading { 15 | color: inherit; 16 | } 17 | 18 | &:hover, 19 | &:focus { 20 | color: $color; 21 | background-color: darken($background, 5%); 22 | } 23 | &.active, 24 | &.active:hover, 25 | &.active:focus { 26 | color: #fff; 27 | background-color: $color; 28 | border-color: $color; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/test/scala/me/yangbajing/ps/data/record/SchemasTest.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.data.record 2 | 3 | import me.yangbajing.UnitPlaySpec 4 | import org.scalatestplus.play.OneAppPerSuite 5 | 6 | /** 7 | * Schemas Test 8 | * Created by jingyang on 15/7/17. 9 | */ 10 | class SchemasTest extends UnitPlaySpec with OneAppPerSuite { 11 | "Schemas ddl" should { 12 | val schemas = app.injector.instanceOf[Schemas] 13 | import schemas.driver.api._ 14 | 15 | "drop tables" in { 16 | val result = schemas.run(schemas.schema.drop.transactionally).futureValue 17 | result.mustBe(()) 18 | } 19 | 20 | "create tables" in { 21 | val result = schemas.run(schemas.schema.create.transactionally).futureValue 22 | result.mustBe(()) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_table-row.scss: -------------------------------------------------------------------------------- 1 | // Tables 2 | 3 | @mixin table-row-variant($state, $background) { 4 | // Exact selectors below required to override `.table-striped` and prevent 5 | // inheritance to nested tables. 6 | .table > thead > tr, 7 | .table > tbody > tr, 8 | .table > tfoot > tr { 9 | > td.#{$state}, 10 | > th.#{$state}, 11 | &.#{$state} > td, 12 | &.#{$state} > th { 13 | background-color: $background; 14 | } 15 | } 16 | 17 | // Hover states for `.table-hover` 18 | // Note: this is not available for cells or rows within `thead` or `tfoot`. 19 | .table-hover > tbody > tr { 20 | > td.#{$state}:hover, 21 | > th.#{$state}:hover, 22 | &.#{$state}:hover > td, 23 | &:hover > .#{$state}, 24 | &.#{$state}:hover > th { 25 | background-color: darken($background, 5%); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-util/src/main/resources/reference.conf: -------------------------------------------------------------------------------- 1 | akka { 2 | loggers = ["akka.event.slf4j.Slf4jLogger"] 3 | loglevel = INFO 4 | fork-join-executor { 5 | # The parallelism factor is used to determine thread pool size using the 6 | # following formula: ceil(available processors * factor). Resulting size 7 | # is then bounded by the parallelism-min and parallelism-max values. 8 | parallelism-factor = 3.0 9 | 10 | # Min number of threads to cap factor-based parallelism number to 11 | parallelism-min = 8 12 | 13 | # Max number of threads to cap factor-based parallelism number to 14 | parallelism-max = 64 15 | } 16 | } 17 | 18 | play-seed { 19 | filter-after-token { 20 | match-all = false 21 | paths = ["/inapi"] 22 | exclude-paths = [ 23 | "/inapi/user/signin", 24 | "/inapi/user/signup" 25 | ] 26 | } 27 | 28 | cache { 29 | host = "localhost" 30 | port = 6379 31 | database = 0 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-util/src/main/scala/me/yangbajing/ps/util/RandomUtils.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.util 2 | 3 | import java.security.SecureRandom 4 | 5 | /** 6 | * 随机资源工具 7 | * Created by jingyang on 15/7/16. 8 | */ 9 | object RandomUtils { 10 | private val LOW = 33 11 | private val HIGH = 127 12 | private val RANDOM_STRING = (0 to 9).mkString + ('a' to 'z').mkString + ('A' to 'Z').mkString + ",.?!~`@#$%^&*()_-+=|[]{};:<>/" 13 | private val RANDOM_STRING_LENGTH = RANDOM_STRING.length 14 | 15 | val random = new SecureRandom() 16 | 17 | def randomString(size: Int): String = { 18 | assert(size > 0, s"size: $size must be > 0") 19 | (0 until size).map(_ => RANDOM_STRING.charAt(random.nextInt(RANDOM_STRING_LENGTH))).mkString 20 | } 21 | 22 | def nextPrintableChar(): Char = { 23 | (random.nextInt(HIGH - LOW) + LOW).toChar 24 | } 25 | 26 | def randomNextInt(begin: Int, bound: Int) = { 27 | random.nextInt(bound - begin) + begin 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_close.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: ($font-size-base * 1.5); 9 | font-weight: $close-font-weight; 10 | line-height: 1; 11 | color: $close-color; 12 | text-shadow: $close-text-shadow; 13 | @include opacity(.2); 14 | 15 | &:hover, 16 | &:focus { 17 | color: $close-color; 18 | text-decoration: none; 19 | cursor: pointer; 20 | @include opacity(.5); 21 | } 22 | 23 | // [converter] extracted button& to button.close 24 | } 25 | 26 | // Additional properties for button version 27 | // iOS requires the button element instead of an anchor tag. 28 | // If you want the anchor version, it requires `href="#"`. 29 | // See https://developer.mozilla.org/en-US/docs/Web/Events/click#Safari_Mobile 30 | button.close { 31 | padding: 0; 32 | cursor: pointer; 33 | background: transparent; 34 | border: 0; 35 | -webkit-appearance: none; 36 | } 37 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_component-animations.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | // Heads up! 6 | // 7 | // We don't use the `.opacity()` mixin here since it causes a bug with text 8 | // fields in IE7-8. Source: https://github.com/twbs/bootstrap/pull/3552. 9 | 10 | .fade { 11 | opacity: 0; 12 | @include transition(opacity .15s linear); 13 | &.in { 14 | opacity: 1; 15 | } 16 | } 17 | 18 | .collapse { 19 | display: none; 20 | 21 | &.in { display: block; } 22 | // [converter] extracted tr&.in to tr.collapse.in 23 | // [converter] extracted tbody&.in to tbody.collapse.in 24 | } 25 | 26 | tr.collapse.in { display: table-row; } 27 | 28 | tbody.collapse.in { display: table-row-group; } 29 | 30 | .collapsing { 31 | position: relative; 32 | height: 0; 33 | overflow: hidden; 34 | @include transition-property(height, visibility); 35 | @include transition-duration(.35s); 36 | @include transition-timing-function(ease); 37 | } 38 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_utilities.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // Floats 7 | // ------------------------- 8 | 9 | .clearfix { 10 | @include clearfix; 11 | } 12 | .center-block { 13 | @include center-block; 14 | } 15 | .pull-right { 16 | float: right !important; 17 | } 18 | .pull-left { 19 | float: left !important; 20 | } 21 | 22 | 23 | // Toggling content 24 | // ------------------------- 25 | 26 | // Note: Deprecated .hide in favor of .hidden or .sr-only (as appropriate) in v3.0.1 27 | .hide { 28 | display: none !important; 29 | } 30 | .show { 31 | display: block !important; 32 | } 33 | .invisible { 34 | visibility: hidden; 35 | } 36 | .text-hide { 37 | @include text-hide; 38 | } 39 | 40 | 41 | // Hide from screenreaders and browsers 42 | // 43 | // Credit: HTML5 Boilerplate 44 | 45 | .hidden { 46 | display: none !important; 47 | } 48 | 49 | 50 | // For Affix plugin 51 | // ------------------------- 52 | 53 | .affix { 54 | position: fixed; 55 | } 56 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-business/src/main/scala/me/yangbajing/ps/business/service/CacheService.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.business.service 2 | 3 | import javax.inject.{Inject, Singleton} 4 | 5 | import akka.actor.ActorSystem 6 | import com.redis.RedisClientPool 7 | import me.yangbajing.ps.setting.SettingComponent 8 | 9 | import scala.concurrent.duration._ 10 | 11 | /** 12 | * 缓存服务 13 | * Created by jingyang on 15/7/17. 14 | */ 15 | @Singleton 16 | class CacheService @Inject()(settingComponent: SettingComponent) 17 | (implicit actorSystem: ActorSystem) { 18 | private val pool = 19 | new RedisClientPool(settingComponent.cache.host, settingComponent.cache.port, settingComponent.cache.database) 20 | 21 | def remove(key: String) = pool.withClient(client => client.del(key)) 22 | 23 | def getString(key: String): Option[String] = pool.withClient(client => client.get(key)) 24 | 25 | def set(key: String, value: String, duration: Duration): Boolean = 26 | pool.withClient(client => client.setex(key, duration.toSeconds.toInt, value)) 27 | 28 | } 29 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-business/src/main/scala/me/yangbajing/ps/business/service/UserService.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.business.service 2 | 3 | import javax.inject.{Inject, Singleton} 4 | 5 | import me.yangbajing.ps.data.domain.RegisterParam 6 | import me.yangbajing.ps.data.repo.UserRepo 7 | 8 | import scala.concurrent.{ExecutionContext, Future} 9 | 10 | /** 11 | * 用户服务 12 | * Created by jingyang on 15/7/16. 13 | */ 14 | @Singleton 15 | class UserService @Inject()(userRepo: UserRepo) 16 | (implicit ec: ExecutionContext) { 17 | 18 | def signin(param: RegisterParam): Future[Option[UserRepo.UserInfo]] = { 19 | if (param.email.isDefined) 20 | userRepo.signinByEmail(param.email.get, param.password) 21 | else if (param.phone.isDefined) 22 | userRepo.signinByPhone(param.phone.get, param.password) 23 | else 24 | throw new IllegalArgumentException("email和phone至少传一个") 25 | } 26 | 27 | def register(param: RegisterParam): Future[UserRepo.UserInfo] = { 28 | userRepo.register(param.email, param.phone, param.password) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_media.scss: -------------------------------------------------------------------------------- 1 | .media { 2 | // Proper spacing between instances of .media 3 | margin-top: 15px; 4 | 5 | &:first-child { 6 | margin-top: 0; 7 | } 8 | } 9 | 10 | .media, 11 | .media-body { 12 | zoom: 1; 13 | overflow: hidden; 14 | } 15 | 16 | .media-body { 17 | width: 10000px; 18 | } 19 | 20 | .media-object { 21 | display: block; 22 | } 23 | 24 | .media-right, 25 | .media > .pull-right { 26 | padding-left: 10px; 27 | } 28 | 29 | .media-left, 30 | .media > .pull-left { 31 | padding-right: 10px; 32 | } 33 | 34 | .media-left, 35 | .media-right, 36 | .media-body { 37 | display: table-cell; 38 | vertical-align: top; 39 | } 40 | 41 | .media-middle { 42 | vertical-align: middle; 43 | } 44 | 45 | .media-bottom { 46 | vertical-align: bottom; 47 | } 48 | 49 | // Reset margins on headings for tighter default spacing 50 | .media-heading { 51 | margin-top: 0; 52 | margin-bottom: 5px; 53 | } 54 | 55 | // Media list variation 56 | // 57 | // Undo default ul/ol styles 58 | .media-list { 59 | padding-left: 0; 60 | list-style: none; 61 | } 62 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_thumbnails.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Thumbnails 3 | // -------------------------------------------------- 4 | 5 | 6 | // Mixin and adjust the regular image class 7 | .thumbnail { 8 | display: block; 9 | padding: $thumbnail-padding; 10 | margin-bottom: $line-height-computed; 11 | line-height: $line-height-base; 12 | background-color: $thumbnail-bg; 13 | border: 1px solid $thumbnail-border; 14 | border-radius: $thumbnail-border-radius; 15 | @include transition(border .2s ease-in-out); 16 | 17 | > img, 18 | a > img { 19 | @include img-responsive; 20 | margin-left: auto; 21 | margin-right: auto; 22 | } 23 | 24 | // [converter] extracted a&:hover, a&:focus, a&.active to a.thumbnail:hover, a.thumbnail:focus, a.thumbnail.active 25 | 26 | // Image captions 27 | .caption { 28 | padding: $thumbnail-caption-padding; 29 | color: $thumbnail-caption-color; 30 | } 31 | } 32 | 33 | // Add a hover state for linked versions only 34 | a.thumbnail:hover, 35 | a.thumbnail:focus, 36 | a.thumbnail.active { 37 | border-color: $link-color; 38 | } 39 | -------------------------------------------------------------------------------- /slick-postgresql-redis/README.md: -------------------------------------------------------------------------------- 1 | # play-seed 2 | 3 | - 作者:杨景(羊八井) 4 | - 邮箱:yangbajing@gmail.com 5 | - 主页:http://www.yangbajing.me 6 | - 微博:http://weibo.com/yangbajing 7 | 8 | - play 2.5 9 | - slick 3.1 10 | - slick-pg 0.14 11 | - scalatest 2.6 12 | - gulp 13 | - redisreact 3.1 14 | - postgresql 9.4 15 | 16 | 实现了简单的用户认证和`session`控制功能。采用`redis`来保存`session`值。自定义`play action`和`play filter`来判断`session`有效性和重设`session`。`session`使用`cookie`实现。 17 | 18 | 数据库层,使用`slick`和`slick-pg`来连接`PostgreSQL`。由`typesafe config`来定义连接参数。 19 | `[ps-util/src/main/resources/reference.conf](https://github.com/yangbajing/play-seed/blob/master/ps-util/src/main/resources/reference.conf)`。 20 | 21 | 定义play监听端口:`PlayKeys.playDefaultPort := 58082`。 22 | 23 | 修改相关配置后,执行如下命令生成数据库实例并启动服务: 24 | 25 | ``` 26 | ./sbt 27 | play-seed-root > ps-data/testOnly me.yangbajing.ps.data.record.SchemasTest 28 | play-seed-root > project ps-web 29 | [ps-web] $ ps-web/run 30 | ``` 31 | 32 | 前端代码使用gulp管理,代码放在`static`目录。编译后的静态文件将保存在`public` 33 | 34 | ``` 35 | npm install 36 | npm run build 37 | ``` 38 | 39 | 访问:[http://localhost:58082/account/signup](http://localhost:58082/account/signup) 注册账号 40 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_messager.scss: -------------------------------------------------------------------------------- 1 | 2 | //== Messager 3 | // 4 | //## 5 | 6 | //** Messager max width 7 | $messager-max-width: 600px !default; 8 | $messager-opacity: .9 !default; 9 | $messager-top-base: 0 !default; 10 | $messager-bottom-base: 0 !default; 11 | 12 | .messager { 13 | max-width: $messager-max-width; 14 | opacity: $messager-opacity; 15 | position: fixed; 16 | padding: 0; 17 | margin-left: auto; 18 | margin-right: auto; 19 | right: 0; 20 | left: 0; 21 | z-index: 99999; 22 | 23 | &.messager-top { 24 | top: $messager-top-base; 25 | } 26 | 27 | &.messager-bottom { 28 | bottom: $messager-bottom-base; 29 | } 30 | 31 | > * { 32 | border-radius: 0; 33 | &:first-child { 34 | border-top-left-radius: $border-radius-base; 35 | border-top-right-radius: $border-radius-base; 36 | } 37 | 38 | &:last-child { 39 | border-bottom-left-radius: $border-radius-base; 40 | border-bottom-right-radius: $border-radius-base; 41 | } 42 | 43 | } 44 | 45 | .alert { 46 | margin-bottom: 0; 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_pager.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Pager pagination 3 | // -------------------------------------------------- 4 | 5 | 6 | .pager { 7 | padding-left: 0; 8 | margin: $line-height-computed 0; 9 | list-style: none; 10 | text-align: center; 11 | @include clearfix; 12 | li { 13 | display: inline; 14 | > a, 15 | > span { 16 | display: inline-block; 17 | padding: 5px 14px; 18 | background-color: $pager-bg; 19 | border: 1px solid $pager-border; 20 | border-radius: $pager-border-radius; 21 | } 22 | 23 | > a:hover, 24 | > a:focus { 25 | text-decoration: none; 26 | background-color: $pager-hover-bg; 27 | } 28 | } 29 | 30 | .next { 31 | > a, 32 | > span { 33 | float: right; 34 | } 35 | } 36 | 37 | .previous { 38 | > a, 39 | > span { 40 | float: left; 41 | } 42 | } 43 | 44 | .disabled { 45 | > a, 46 | > a:hover, 47 | > a:focus, 48 | > span { 49 | color: $pager-disabled-color; 50 | background-color: $pager-bg; 51 | cursor: $cursor-disabled; 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------------------------------- 3 | 4 | // Utilities 5 | @import "mixins/hide-text"; 6 | @import "mixins/opacity"; 7 | @import "mixins/image"; 8 | @import "mixins/labels"; 9 | @import "mixins/reset-filter"; 10 | @import "mixins/resize"; 11 | @import "mixins/responsive-visibility"; 12 | @import "mixins/size"; 13 | @import "mixins/tab-focus"; 14 | @import "mixins/text-emphasis"; 15 | @import "mixins/text-overflow"; 16 | @import "mixins/vendor-prefixes"; 17 | 18 | // Components 19 | @import "mixins/alerts"; 20 | @import "mixins/buttons"; 21 | @import "mixins/panels"; 22 | @import "mixins/pagination"; 23 | @import "mixins/list-group"; 24 | @import "mixins/nav-divider"; 25 | @import "mixins/forms"; 26 | @import "mixins/progress-bar"; 27 | @import "mixins/table-row"; 28 | 29 | // Skins 30 | @import "mixins/background-variant"; 31 | @import "mixins/border-radius"; 32 | @import "mixins/gradients"; 33 | 34 | // Layout 35 | @import "mixins/clearfix"; 36 | @import "mixins/center-block"; 37 | @import "mixins/nav-vertical-align"; 38 | @import "mixins/grid-framework"; 39 | @import "mixins/grid"; 40 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-util/src/main/scala/me/yangbajing/ps/util/Utils.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.util 2 | 3 | import org.apache.commons.codec.digest.DigestUtils 4 | 5 | /** 6 | * 工具 7 | * Created by jingyang on 15/7/16. 8 | */ 9 | object Utils { 10 | def option[T <: CharSequence](v: T): Option[T] = Option(v).filterNot(_.length() == 0) 11 | 12 | @inline 13 | def generatePassword(pwd: String): (Array[Byte], Array[Byte]) = 14 | generatePassword(pwd.getBytes(Constants.CHARSET)) 15 | 16 | def generatePassword(pwd: Array[Byte]): (Array[Byte], Array[Byte]) = { 17 | val salt = Array.ofDim[Byte](Constants.SALT_LENGTH) 18 | RandomUtils.random.nextBytes(salt) 19 | val digest = DigestUtils.getSha256Digest 20 | digest.update(salt) 21 | digest.update(pwd) 22 | (salt, digest.digest()) 23 | } 24 | 25 | @inline 26 | def generatePassword(salt: Array[Byte], pwd: String): Array[Byte] = 27 | generatePassword(salt, pwd.getBytes(Constants.CHARSET)) 28 | 29 | def generatePassword(salt: Array[Byte], pwd: Array[Byte]): Array[Byte] = { 30 | val digest = DigestUtils.getSha256Digest 31 | digest.update(salt) 32 | digest.update(pwd) 33 | digest.digest() 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/app/controllers/UserAction.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import javax.inject.Inject 4 | 5 | import me.yangbajing.ps.business.service.CacheService 6 | import me.yangbajing.ps.data.domain.OwnerToken 7 | import play.api.mvc._ 8 | 9 | import scala.concurrent.Future 10 | 11 | case class UserTokenRequest[A](userToken: OwnerToken, request: Request[A]) extends WrappedRequest[A](request) 12 | 13 | class UserAction @Inject()(cacheService: CacheService, 14 | webTools: WebTools 15 | ) extends ActionBuilder[UserTokenRequest] with ActionRefiner[Request, UserTokenRequest] { 16 | 17 | override protected def refine[A](request: Request[A]): Future[Either[Result, UserTokenRequest[A]]] = { 18 | val result = webTools.getUserToken(request) 19 | .map { 20 | case Right(token) => 21 | Right(UserTokenRequest(token, request)) 22 | case Left(key) => 23 | cacheService.remove(key) 24 | Left(Results.Redirect(controllers.routes.PageController.signin())) 25 | } 26 | .getOrElse(Left(Results.Redirect(controllers.routes.PageController.signin()))) 27 | Future.successful(result) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/app/controllers/PageController.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import javax.inject.{Inject, Singleton} 4 | 5 | import play.api.Environment 6 | import play.api.http.Writeable 7 | import play.api.mvc.{Action, Controller} 8 | 9 | /** 10 | * 页面 11 | * Created by jingyang on 15/7/17. 12 | */ 13 | @Singleton 14 | class PageController @Inject()(webTools: WebTools, 15 | environment: Environment, 16 | userAction: UserAction 17 | ) extends Controller with BaseController { 18 | 19 | def signin = signPage(views.html.account.signin()) 20 | 21 | def signup = signPage(views.html.account.signup()) 22 | 23 | def homeMain = userAction { request => 24 | Ok(views.html.home.main(request.userToken)) 25 | } 26 | 27 | private def signPage[C](content: C)(implicit writeable: Writeable[C]) = Action { implicit request => 28 | webTools.getUserToken 29 | .map { 30 | case Right(_) => 31 | Redirect(controllers.routes.PageController.homeMain()) 32 | case Left(key) => 33 | Ok(content).withCookies(webTools.discardingSession(key)) 34 | } 35 | .getOrElse(Ok(content)) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_jumbotron.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Jumbotron 3 | // -------------------------------------------------- 4 | 5 | 6 | .jumbotron { 7 | padding: $jumbotron-padding ($jumbotron-padding / 2); 8 | margin-bottom: $jumbotron-padding; 9 | color: $jumbotron-color; 10 | background-color: $jumbotron-bg; 11 | 12 | h1, 13 | .h1 { 14 | color: $jumbotron-heading-color; 15 | } 16 | 17 | p { 18 | margin-bottom: ($jumbotron-padding / 2); 19 | font-size: $jumbotron-font-size; 20 | font-weight: 200; 21 | } 22 | 23 | > hr { 24 | border-top-color: darken($jumbotron-bg, 10%); 25 | } 26 | 27 | .container &, 28 | .container-fluid & { 29 | border-radius: $border-radius-large; // Only round corners at higher resolutions if contained in a container 30 | } 31 | 32 | .container { 33 | max-width: 100%; 34 | } 35 | 36 | @media screen and (min-width: $screen-sm-min) { 37 | padding: ($jumbotron-padding * 1.6) 0; 38 | 39 | .container &, 40 | .container-fluid & { 41 | padding-left: ($jumbotron-padding * 2); 42 | padding-right: ($jumbotron-padding * 2); 43 | } 44 | 45 | h1, 46 | .h1 { 47 | font-size: ($font-size-base * 4.5); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/bootstrap.scss: -------------------------------------------------------------------------------- 1 | $border-radius-base: 0; 2 | $border-radius-large: 0; 3 | $border-radius-small: 0; 4 | 5 | // Core variables and mixins 6 | @import "variables"; 7 | @import "mixins"; 8 | // Reset and dependencies 9 | @import "normalize"; 10 | @import "print"; 11 | // Core CSS 12 | @import "scaffolding"; 13 | @import "type"; 14 | @import "code"; 15 | @import "grid"; 16 | @import "tables"; 17 | @import "forms"; 18 | @import "buttons"; 19 | // Components 20 | @import "component-animations"; 21 | @import "dropdowns"; 22 | @import "button-groups"; 23 | @import "input-groups"; 24 | @import "navs"; 25 | @import "navbar"; 26 | @import "breadcrumbs"; 27 | @import "pagination"; 28 | @import "pager"; 29 | @import "labels"; 30 | @import "badges"; 31 | @import "jumbotron"; 32 | @import "thumbnails"; 33 | @import "alerts"; 34 | @import "progress-bars"; 35 | @import "media"; 36 | @import "list-group"; 37 | @import "panels"; 38 | @import "responsive-embed"; 39 | @import "wells"; 40 | @import "close"; 41 | @import "messager"; 42 | // Components w/ JavaScript 43 | @import "modals"; 44 | @import "tooltip"; 45 | @import "popovers"; 46 | @import "carousel"; 47 | // Utility classes 48 | @import "utilities"; 49 | @import "responsive-utilities"; 50 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_buttons.scss: -------------------------------------------------------------------------------- 1 | // Button variants 2 | // 3 | // Easily pump out default styles, as well as :hover, :focus, :active, 4 | // and disabled options for all buttons 5 | 6 | @mixin button-variant($color, $background, $border) { 7 | color: $color; 8 | background-color: $background; 9 | border-color: $border; 10 | 11 | &:hover, 12 | &:focus, 13 | &.focus, 14 | &:active, 15 | &.active, 16 | .open > &.dropdown-toggle { 17 | color: $color; 18 | background-color: darken($background, 10%); 19 | border-color: darken($border, 12%); 20 | } 21 | &:active, 22 | &.active, 23 | .open > &.dropdown-toggle { 24 | background-image: none; 25 | } 26 | &.disabled, 27 | &[disabled], 28 | fieldset[disabled] & { 29 | &, 30 | &:hover, 31 | &:focus, 32 | &.focus, 33 | &:active, 34 | &.active { 35 | background-color: $background; 36 | border-color: $border; 37 | } 38 | } 39 | 40 | .badge { 41 | color: $background; 42 | background-color: $color; 43 | } 44 | } 45 | 46 | // Button sizes 47 | @mixin button-size($padding-vertical, $padding-horizontal, $font-size, $line-height, $border-radius) { 48 | padding: $padding-vertical $padding-horizontal; 49 | font-size: $font-size; 50 | line-height: $line-height; 51 | border-radius: $border-radius; 52 | } 53 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_image.scss: -------------------------------------------------------------------------------- 1 | // Image Mixins 2 | // - Responsive image 3 | // - Retina image 4 | 5 | 6 | // Responsive image 7 | // 8 | // Keep images from scaling beyond the width of their parents. 9 | @mixin img-responsive($display: block) { 10 | display: $display; 11 | max-width: 100%; // Part 1: Set a maximum relative to the parent 12 | height: auto; // Part 2: Scale the height according to the width, otherwise you get stretching 13 | } 14 | 15 | 16 | // Retina image 17 | // 18 | // Short retina mixin for setting background-image and -size. Note that the 19 | // spelling of `min--moz-device-pixel-ratio` is intentional. 20 | @mixin img-retina($file-1x, $file-2x, $width-1x, $height-1x) { 21 | background-image: url(if($bootstrap-sass-asset-helper, twbs-image-path("#{$file-1x}"), "#{$file-1x}")); 22 | 23 | @media 24 | only screen and (-webkit-min-device-pixel-ratio: 2), 25 | only screen and ( min--moz-device-pixel-ratio: 2), 26 | only screen and ( -o-min-device-pixel-ratio: 2/1), 27 | only screen and ( min-device-pixel-ratio: 2), 28 | only screen and ( min-resolution: 192dpi), 29 | only screen and ( min-resolution: 2dppx) { 30 | background-image: url(if($bootstrap-sass-asset-helper, twbs-image-path("#{$file-2x}"), "#{$file-2x}")); 31 | background-size: $width-1x $height-1x; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/app/views/account/signin.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 | @foot = { 3 | 4 | } 5 | @views.html._layout("登陆", foot = foot) { 6 |
7 |
8 |
9 |
10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 |
19 |

20 | 21 |

22 | 没有账号,请注册 23 |

24 |
25 |
26 |
27 |
28 |
29 | } 30 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/main/scala/me/yangbajing/ps/data/domain/CaseClass.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.data.domain 2 | 3 | import play.api.libs.json.Json 4 | 5 | /** 6 | * Created by Yang Jing (yangbajing@gmail.com) on 2016-01-04. 7 | */ 8 | case class CaseClass(a: String, 9 | b: String = "", 10 | c: String = "", 11 | d: String = "", 12 | e: String = "", 13 | f: String = "", 14 | g: String = "", 15 | h: String = "", 16 | i: String = "", 17 | j: String = "", 18 | k: String = "", 19 | // l: String = "", 20 | // m: String = "", 21 | // n: String = "", 22 | // o: String = "", 23 | // p: String = "", 24 | // q: String = "", 25 | // r: String = "", 26 | // s: String = "", 27 | // t: String = "", 28 | // u: String = "", 29 | // v: String = "", 30 | // w: String = "", 31 | // x: String = "", 32 | // y: String = "", 33 | z: String = "") { 34 | def this() { 35 | this("") 36 | } 37 | } 38 | 39 | object CaseClass { 40 | implicit val jsonFormats = Json.format[CaseClass] 41 | } 42 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "play-seed", 3 | "version": "0.0.1", 4 | "description": "play-seed assets(js, css, img, font)", 5 | "repository": "https://github.com/yangbajing/play-seed", 6 | "engines": { 7 | "node": ">=0.12.0" 8 | }, 9 | "scripts": { 10 | "clean": "./node_modules/.bin/gulp clean", 11 | "build": "./node_modules/.bin/gulp clean && ./node_modules/.bin/gulp build", 12 | "build-test": "./node_modules/.bin/gulp clean && ./node_modules/.bin/gulp build --test && ./node_modules/.bin/gulp min", 13 | "build-prod": "./node_modules/.bin/gulp clean && ./node_modules/.bin/gulp build --prod && ./node_modules/.bin/gulp min", 14 | "watch": "./node_modules/.bin/gulp watch", 15 | "wb-start": "webpack-dev-server --hot --progress --colors", 16 | "wb-build": "webpack --progress --colors" 17 | }, 18 | "author": "yangbajing@gmail.com", 19 | "dependencies": { 20 | "bson-objectid": "1.1.1", 21 | "crypto-js": "3.1.4", 22 | "react": "0.13.3", 23 | "react-router": "0.13.3" 24 | }, 25 | "devDependencies": { 26 | "del": "1.2.0", 27 | "gulp": "3.9.0", 28 | "gulp-browserify": "0.5.1", 29 | "gulp-concat": "2.6.0", 30 | "gulp-if": "1.2.5", 31 | "gulp-minify-css": "1.2.0", 32 | "gulp-rename": "1.2.2", 33 | "gulp-replace": "0.5.3", 34 | "gulp-sass": "2.0.4", 35 | "gulp-twig": "0.3.0", 36 | "gulp-uglify": "1.2.0", 37 | "reactify": "1.1.1", 38 | "yargs": "3.15.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/app/utils/ErrorHandler.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import javax.inject.{Inject, Provider} 4 | 5 | import com.typesafe.scalalogging.StrictLogging 6 | import controllers.WebTools 7 | import play.api.http.DefaultHttpErrorHandler 8 | import play.api.libs.json.Json 9 | import play.api.mvc.{RequestHeader, Result, Results} 10 | import play.api.routing.Router 11 | import play.api.{Configuration, Environment, OptionalSourceMapper} 12 | 13 | import scala.concurrent.Future 14 | 15 | /** 16 | * Created by Yang Jing (yangbajing@gmail.com) on 2016-03-07. 17 | */ 18 | class ErrorHandler @Inject()(webTools: WebTools, 19 | env: Environment, 20 | config: Configuration, 21 | sourceMapper: OptionalSourceMapper, 22 | router: Provider[Router] 23 | ) extends DefaultHttpErrorHandler(env, config, sourceMapper, router) with StrictLogging { 24 | 25 | override def onServerError(request: RequestHeader, ex: Throwable): Future[Result] = { 26 | request.path match { 27 | case s if s.startsWith("/inapi/") => 28 | logger.error(ex.toString, ex) 29 | Future.successful { 30 | Results.InternalServerError(Json.obj("code" -> -1, "message" -> ex.getLocalizedMessage)). 31 | withCookies(webTools.discardingSession()) 32 | } 33 | 34 | case _ => 35 | super.onServerError(request, ex) 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_labels.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Labels 3 | // -------------------------------------------------- 4 | 5 | .label { 6 | display: inline; 7 | padding: .2em .6em .3em; 8 | font-size: 75%; 9 | font-weight: bold; 10 | line-height: 1; 11 | color: $label-color; 12 | text-align: center; 13 | white-space: nowrap; 14 | vertical-align: baseline; 15 | border-radius: .25em; 16 | 17 | // [converter] extracted a& to a.label 18 | 19 | // Empty labels collapse automatically (not available in IE8) 20 | &:empty { 21 | display: none; 22 | } 23 | 24 | // Quick fix for labels in buttons 25 | .btn & { 26 | position: relative; 27 | top: -1px; 28 | } 29 | } 30 | 31 | // Add hover effects, but only for links 32 | a.label { 33 | &:hover, 34 | &:focus { 35 | color: $label-link-hover-color; 36 | text-decoration: none; 37 | cursor: pointer; 38 | } 39 | } 40 | 41 | // Colors 42 | // Contextual variations (linked labels get darker on :hover) 43 | 44 | .label-default { 45 | @include label-variant($label-default-bg); 46 | } 47 | 48 | .label-primary { 49 | @include label-variant($label-primary-bg); 50 | } 51 | 52 | .label-success { 53 | @include label-variant($label-success-bg); 54 | } 55 | 56 | .label-info { 57 | @include label-variant($label-info-bg); 58 | } 59 | 60 | .label-warning { 61 | @include label-variant($label-warning-bg); 62 | } 63 | 64 | .label-danger { 65 | @include label-variant($label-danger-bg); 66 | } 67 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_badges.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Badges 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .badge { 8 | display: inline-block; 9 | min-width: 10px; 10 | padding: 3px 7px; 11 | font-size: $font-size-small; 12 | font-weight: $badge-font-weight; 13 | color: $badge-color; 14 | line-height: $badge-line-height; 15 | vertical-align: baseline; 16 | white-space: nowrap; 17 | text-align: center; 18 | background-color: $badge-bg; 19 | border-radius: $badge-border-radius; 20 | 21 | // Empty badges collapse automatically (not available in IE8) 22 | &:empty { 23 | display: none; 24 | } 25 | 26 | // Quick fix for badges in buttons 27 | .btn & { 28 | position: relative; 29 | top: -1px; 30 | } 31 | 32 | .btn-xs &, 33 | .btn-group-xs > .btn & { 34 | top: 0; 35 | padding: 1px 5px; 36 | } 37 | 38 | // [converter] extracted a& to a.badge 39 | 40 | // Account for badges in navs 41 | .list-group-item.active > &, 42 | .nav-pills > .active > a > & { 43 | color: $badge-active-color; 44 | background-color: $badge-active-bg; 45 | } 46 | 47 | .list-group-item > & { 48 | float: right; 49 | } 50 | 51 | .list-group-item > & + & { 52 | margin-right: 5px; 53 | } 54 | 55 | .nav-pills > li > a > & { 56 | margin-left: 3px; 57 | } 58 | } 59 | 60 | // Hover state, but only for links 61 | a.badge { 62 | &:hover, 63 | &:focus { 64 | color: $badge-link-hover-color; 65 | text-decoration: none; 66 | cursor: pointer; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/app/controllers/inapi/UserController.scala: -------------------------------------------------------------------------------- 1 | package controllers.inapi 2 | 3 | import javax.inject.Inject 4 | 5 | import controllers.{BaseController, WebTools} 6 | import me.yangbajing.ps.business.service.UserService 7 | import me.yangbajing.ps.data.domain.{OwnerToken, RegisterParam} 8 | import me.yangbajing.ps.util.Constants 9 | import play.api.libs.json.{JsObject, Json} 10 | import play.api.mvc.Action 11 | 12 | /** 13 | * User API 14 | * Created by jingyang on 15/7/16. 15 | */ 16 | class UserController @Inject()(userService: UserService, 17 | webTools: WebTools) extends BaseController { 18 | 19 | def signup = Action.async(parse.json.map(_.as[RegisterParam])) { request => 20 | userService.register(request.body).map { case (user, email, phone) => 21 | val bo = Json.toJson(user).asInstanceOf[JsObject] ++ Json.obj("email" -> email, "phone" -> phone) 22 | Ok(bo) 23 | } 24 | } 25 | 26 | def signin() = Action.async(parse.json.map(_.as[RegisterParam])) { request => 27 | userService.signin(request.body).map { 28 | case Some((user, email, phone)) => 29 | val bo = Json.toJson(user).asInstanceOf[JsObject] ++ Json.obj("email" -> email, "phone" -> phone) 30 | val cookie = webTools.createSession(OwnerToken(user.id, email, phone, (user.attrs \ Constants.NICK).asOpt[String])) 31 | Ok(bo).withCookies(cookie) 32 | case None => 33 | NotFound 34 | }.recover { 35 | case e: Exception => 36 | logger.debug(e.toString, e) 37 | Unauthorized 38 | } 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/app/views/account/signup.scala.html: -------------------------------------------------------------------------------- 1 | @() 2 | @foot = { 3 | 4 | } 5 | @views.html._layout("注册", foot = foot) { 6 |
7 |
8 |
9 |
10 |
11 | 12 | 13 |
14 |
15 | 16 | 17 |
18 |
19 | 20 | 21 |
22 |
23 |

24 | 25 |

26 | 已有账号 27 |

28 |
29 |
30 |
31 |
32 |
33 | } 34 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/main/scala/me/yangbajing/ps/data/record/DatabaseModule.scala: -------------------------------------------------------------------------------- 1 | //package me.yangbajing.ps.data.record 2 | // 3 | //import java.time.LocalDateTime 4 | // 5 | //import me.yangbajing.ps.data.PsSqlDriver$ 6 | //import me.yangbajing.ps.types.OwnerStatus._ 7 | //import play.api.libs.json.JsValue 8 | // 9 | ///** 10 | // * Created by Yang Jing (yangbajing@gmail.com) on 2016-07-27. 11 | // */ 12 | //trait DatabaseModule { 13 | // val driver: PsSqlDriver 14 | // 15 | // import driver.api._ 16 | // 17 | // class TableUser(tag: Tag) extends Table[User](tag, "t_user") { 18 | // val id = column[Long]("id", O.PrimaryKey) 19 | // val attrs = column[JsValue]("attrs") 20 | // val status = column[OwnerStatus]("status") 21 | // val created_at = column[LocalDateTime]("created_at") 22 | // 23 | // def * = (id, attrs, status, created_at) <> (User.tupled, User.unapply) 24 | // } 25 | // 26 | // class TableCredential(tag: Tag) extends Table[Credential](tag, "t_credential") { 27 | // val id = column[Long]("id", O.PrimaryKey, O.AutoInc) 28 | // val email = column[Option[String]]("email") 29 | // val phone = column[Option[String]]("phone") 30 | // val salt = column[Array[Byte]]("salt") 31 | // val password = column[Array[Byte]]("password") 32 | // val created_at = column[LocalDateTime]("created_at") 33 | // 34 | // def * = (id.?, email, phone, salt, password, created_at) <> (Credential.tupled, Credential.unapply) 35 | // } 36 | // 37 | // def tCredential = TableQuery[TableCredential] 38 | // 39 | // def tUser = TableQuery[TableUser] 40 | // 41 | // def schema = 42 | // tCredential.schema ++ 43 | // tUser.schema 44 | //} 45 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_code.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Code (inline and block) 3 | // -------------------------------------------------- 4 | 5 | 6 | // Inline and block code styles 7 | code, 8 | kbd, 9 | pre, 10 | samp { 11 | font-family: $font-family-monospace; 12 | } 13 | 14 | // Inline code 15 | code { 16 | padding: 2px 4px; 17 | font-size: 90%; 18 | color: $code-color; 19 | background-color: $code-bg; 20 | border-radius: $border-radius-base; 21 | } 22 | 23 | // User input typically entered via keyboard 24 | kbd { 25 | padding: 2px 4px; 26 | font-size: 90%; 27 | color: $kbd-color; 28 | background-color: $kbd-bg; 29 | border-radius: $border-radius-small; 30 | box-shadow: inset 0 -1px 0 rgba(0,0,0,.25); 31 | 32 | kbd { 33 | padding: 0; 34 | font-size: 100%; 35 | font-weight: bold; 36 | box-shadow: none; 37 | } 38 | } 39 | 40 | // Blocks of code 41 | pre { 42 | display: block; 43 | padding: (($line-height-computed - 1) / 2); 44 | margin: 0 0 ($line-height-computed / 2); 45 | font-size: ($font-size-base - 1); // 14px to 13px 46 | line-height: $line-height-base; 47 | word-break: break-all; 48 | word-wrap: break-word; 49 | color: $pre-color; 50 | background-color: $pre-bg; 51 | border: 1px solid $pre-border-color; 52 | border-radius: $border-radius-base; 53 | 54 | // Account for some code outputs that place code tags in pre tags 55 | code { 56 | padding: 0; 57 | font-size: inherit; 58 | color: inherit; 59 | white-space: pre-wrap; 60 | background-color: transparent; 61 | border-radius: 0; 62 | } 63 | } 64 | 65 | // Enable scrollable blocks of code 66 | .pre-scrollable { 67 | max-height: $pre-scrollable-max-height; 68 | overflow-y: scroll; 69 | } 70 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/js/sign.js: -------------------------------------------------------------------------------- 1 | jQuery(function () { 2 | 3 | jQuery('#signin-form').on('submit', function (evt) { 4 | evt.preventDefault(); 5 | var thiz = jQuery(this); 6 | var payload = PS.form.serialize(thiz); 7 | payload.password = PS.util.sha1Hex(payload.password); 8 | 9 | PS.ajax.post('/inapi/user/signin', payload, 'json') 10 | .done(function (data, status, jqXhr) { 11 | setTimeout(function () { 12 | window.location.href = '/home'; 13 | }, 2000); 14 | thiz.find('.text-info').html('登录成功,2秒后跳转'); 15 | }) 16 | .fail(function (data, status, jqXhr) { 17 | alert('登录失败'); 18 | }); 19 | }); 20 | 21 | jQuery('#signup-form').on('submit', function (evt) { 22 | evt.preventDefault(); 23 | var thiz = jQuery(this); 24 | var payload = PS.form.serialize(thiz); 25 | if (payload.password.length < 6 || payload.password.length > 32) { 26 | alert('密码为6至32个字符'); 27 | return; 28 | } 29 | if (payload.password !== payload.passwordRepeat) { 30 | alert('两次密码不匹配'); 31 | return; 32 | } 33 | payload.password = PS.util.sha1Hex(payload.password); 34 | payload.passwordRepeat = undefined; 35 | 36 | PS.ajax.post('/inapi/user/signup', payload, 'json') 37 | .done(function (data, status, jqXhr) { 38 | setTimeout(function () { 39 | window.location.href = '/account/signin'; 40 | }, 2000); 41 | thiz.find('.text-info').html('注册成功,2秒后跳转到登录页面'); 42 | }) 43 | .fail(function (data, status, jqXhr) { 44 | alert('注册失败'); 45 | }); 46 | }) 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_grid.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Grid system 3 | // -------------------------------------------------- 4 | 5 | 6 | // Container widths 7 | // 8 | // Set the container width, and override it for fixed navbars in media queries. 9 | 10 | .container { 11 | @include container-fixed; 12 | 13 | @media (min-width: $screen-sm-min) { 14 | width: $container-sm; 15 | } 16 | @media (min-width: $screen-md-min) { 17 | width: $container-md; 18 | } 19 | @media (min-width: $screen-lg-min) { 20 | width: $container-lg; 21 | } 22 | } 23 | 24 | 25 | // Fluid container 26 | // 27 | // Utilizes the mixin meant for fixed width containers, but without any defined 28 | // width for fluid, full width layouts. 29 | 30 | .container-fluid { 31 | @include container-fixed; 32 | } 33 | 34 | 35 | // Row 36 | // 37 | // Rows contain and clear the floats of your columns. 38 | 39 | .row { 40 | @include make-row; 41 | } 42 | 43 | 44 | // Columns 45 | // 46 | // Common styles for small and large grid columns 47 | 48 | @include make-grid-columns; 49 | 50 | 51 | // Extra small grid 52 | // 53 | // Columns, offsets, pushes, and pulls for extra small devices like 54 | // smartphones. 55 | 56 | @include make-grid(xs); 57 | 58 | 59 | // Small grid 60 | // 61 | // Columns, offsets, pushes, and pulls for the small device range, from phones 62 | // to tablets. 63 | 64 | @media (min-width: $screen-sm-min) { 65 | @include make-grid(sm); 66 | } 67 | 68 | 69 | // Medium grid 70 | // 71 | // Columns, offsets, pushes, and pulls for the desktop device range. 72 | 73 | @media (min-width: $screen-md-min) { 74 | @include make-grid(md); 75 | } 76 | 77 | 78 | // Large grid 79 | // 80 | // Columns, offsets, pushes, and pulls for the large desktop device range. 81 | 82 | @media (min-width: $screen-lg-min) { 83 | @include make-grid(lg); 84 | } 85 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_alerts.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Alerts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // ------------------------- 8 | 9 | .alert { 10 | padding: $alert-padding; 11 | margin-bottom: $line-height-computed; 12 | border: 1px solid transparent; 13 | border-radius: $alert-border-radius; 14 | 15 | // Headings for larger alerts 16 | h4 { 17 | margin-top: 0; 18 | // Specified for the h4 to prevent conflicts of changing $headings-color 19 | color: inherit; 20 | } 21 | 22 | // Provide class for links that match alerts 23 | .alert-link { 24 | font-weight: $alert-link-font-weight; 25 | } 26 | 27 | // Improve alignment and spacing of inner content 28 | > p, 29 | > ul { 30 | margin-bottom: 0; 31 | } 32 | 33 | > p + p { 34 | margin-top: 5px; 35 | } 36 | } 37 | 38 | // Dismissible alerts 39 | // 40 | // Expand the right padding and account for the close button's positioning. 41 | 42 | .alert-dismissable, // The misspelled .alert-dismissable was deprecated in 3.2.0. 43 | .alert-dismissible { 44 | padding-right: ($alert-padding + 20); 45 | 46 | // Adjust close link position 47 | .close { 48 | position: relative; 49 | top: -2px; 50 | right: -21px; 51 | color: inherit; 52 | } 53 | } 54 | 55 | // Alternate styles 56 | // 57 | // Generate contextual modifier classes for colorizing the alert. 58 | 59 | .alert-success { 60 | @include alert-variant($alert-success-bg, $alert-success-border, $alert-success-text); 61 | } 62 | 63 | .alert-info { 64 | @include alert-variant($alert-info-bg, $alert-info-border, $alert-info-text); 65 | } 66 | 67 | .alert-warning { 68 | @include alert-variant($alert-warning-bg, $alert-warning-border, $alert-warning-text); 69 | } 70 | 71 | .alert-danger { 72 | @include alert-variant($alert-danger-bg, $alert-danger-border, $alert-danger-text); 73 | } 74 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/main/scala/me/yangbajing/ps/data/PsSqlDriver.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.data 2 | 3 | import com.github.tminglei.slickpg._ 4 | import com.github.tminglei.slickpg.utils.SimpleArrayUtils 5 | import me.yangbajing.ps.types.OwnerStatus 6 | import play.api.libs.json.{JsValue, Json} 7 | import slick.driver.JdbcDriver 8 | 9 | /** 10 | * Slick JDBC Driver 11 | * Created by jingyang on 15/7/16. 12 | */ 13 | 14 | trait CustomColumnTypes { 15 | this: JdbcDriver => 16 | 17 | trait CustomColumnsImplicits { 18 | self: API => 19 | implicit val __ownerStatusColumnType = MappedColumnType.base[OwnerStatus.Value, String](_.toString, OwnerStatus.withName) 20 | } 21 | 22 | } 23 | 24 | trait PsSqlDriver 25 | extends ExPostgresDriver 26 | with PgDate2Support 27 | with PgHStoreSupport 28 | with PgPlayJsonSupport 29 | with PgArraySupport 30 | //with PgRangeSupport 31 | //with PgSearchSupport 32 | //with PgPostGISSupport 33 | with CustomColumnTypes { 34 | 35 | object MyAPI 36 | extends API 37 | with DateTimeImplicits 38 | with ArrayImplicits 39 | with PlayJsonImplicits 40 | with HStoreImplicits 41 | with CustomColumnsImplicits { 42 | implicit val strListTypeMapper = new SimpleArrayJdbcType[String]("text").to(_.toList) 43 | implicit val playJsonArrayTypeMapper = new AdvancedArrayJdbcType[JsValue]( 44 | pgjson, 45 | (s) => SimpleArrayUtils.fromString[JsValue](Json.parse)(s).orNull, 46 | (v) => SimpleArrayUtils.mkString[JsValue](_.toString())(v)) 47 | .to(_.toList) 48 | } 49 | 50 | override def pgjson: String = "jsonb" 51 | 52 | override val api = MyAPI 53 | 54 | val plainAPI = new API with Date2DateTimePlainImplicits 55 | with SimpleArrayPlainImplicits 56 | with PlayJsonPlainImplicits 57 | with SimpleHStorePlainImplicits 58 | with CustomColumnsImplicits 59 | 60 | } 61 | 62 | object PsSqlDriver extends PsSqlDriver 63 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-util/src/main/scala/me/yangbajing/ps/util/enums/JsValueEnumUtils.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.util.enums 2 | 3 | import play.api.libs.json._ 4 | import scala.language.implicitConversions 5 | 6 | /** 7 | * play json enum utils 8 | * Created by jingyang on 15/7/16. 9 | */ 10 | object JsValueEnumUtils { 11 | 12 | def enumReads[E <: Enumeration](enum: E): Reads[E#Value] = 13 | new Reads[E#Value] { 14 | def reads(json: JsValue): JsResult[E#Value] = json match { 15 | case JsString(s) => { 16 | try { 17 | JsSuccess(enum.withName(s)) 18 | } catch { 19 | case _: NoSuchElementException => 20 | JsError(s"Enumeration expected of type: '${enum.getClass}', but it does not appear to contain the value: '$s'") 21 | } 22 | } 23 | case _ => JsError("String value expected") 24 | } 25 | } 26 | 27 | def enumReadsId[E <: Enumeration](enum: E): Reads[E#Value] = 28 | new Reads[E#Value] { 29 | def reads(json: JsValue): JsResult[E#Value] = json match { 30 | case JsNumber(s) => { 31 | try { 32 | JsSuccess(enum(s.toInt)) 33 | } catch { 34 | case _: NoSuchElementException => 35 | JsError(s"Enumeration expected of type: '${enum.getClass}', but it does not appear to contain the value: '$s'") 36 | } 37 | } 38 | case _ => JsError("String value expected") 39 | } 40 | } 41 | 42 | implicit def enumWrites[E <: Enumeration]: Writes[E#Value] = 43 | new Writes[E#Value] { 44 | def writes(v: E#Value): JsValue = JsString(v.toString) 45 | } 46 | 47 | implicit def enumFormat[E <: Enumeration](enum: E): Format[E#Value] = { 48 | Format(enumReads(enum), enumWrites) 49 | } 50 | 51 | def enumWritesNumber[E <: Enumeration]: Writes[E#Value] = 52 | new Writes[E#Value] { 53 | def writes(v: E#Value): JsValue = JsNumber(v.id) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-util/src/main/scala/me/yangbajing/ps/setting/SettingComponent.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.setting 2 | 3 | import com.typesafe.config.{ConfigFactory, Config} 4 | import scala.collection.JavaConverters._ 5 | import scala.util.Try 6 | 7 | import javax.inject.Singleton 8 | 9 | /** 10 | * 配置文件 11 | * Created by jingyang on 15/7/16. 12 | */ 13 | @Singleton 14 | class SettingComponent { 15 | val cache = { 16 | val conf = ConfigFactory.load().getConfig("play-seed.cache") 17 | CacheSetting( 18 | getString(conf, "host").get, 19 | getInt(conf, "port").get, 20 | getInt(conf, "database").get) 21 | } 22 | 23 | val filterAfterToken = { 24 | val conf = ConfigFactory.load().getConfig("play-seed.filter-after-token") 25 | FilterAfterToken( 26 | getBoolean(conf, "match-all", false), 27 | getStringList(conf, "paths"), 28 | getStringList(conf, "exclude-paths")) 29 | } 30 | 31 | private def getStringList(conf: Config, name: String): Seq[String] = try { 32 | conf.getStringList(name).asScala 33 | } catch { 34 | case _: Exception => 35 | Nil 36 | } 37 | 38 | private def getString(conf: Config, name: String, default: => String): String = getString(conf, name).getOrElse(default) 39 | 40 | private def getString(conf: Config, name: String): Try[String] = Try(conf.getString(name)) 41 | 42 | private def getBoolean(conf: Config, name: String, default: => Boolean): Boolean = 43 | getBoolean(conf, name).getOrElse(default) 44 | 45 | private def getBoolean(conf: Config, name: String): Try[Boolean] = Try(conf.getBoolean(name)) 46 | 47 | private def getInt(conf: Config, name: String, default: => Int): Int = getInt(conf, name).getOrElse(default) 48 | 49 | private def getInt(conf: Config, name: String): Try[Int] = Try(conf.getInt(name)) 50 | } 51 | 52 | case class Setting(db: Config) 53 | 54 | case class FilterAfterToken(matchAll: Boolean, paths: Seq[String], excludePaths: Seq[String]) 55 | 56 | case class CacheSetting(host: String, port: Int, database: Int) 57 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/main/scala/me/yangbajing/ps/data/record/Schemas.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.data.record 2 | 3 | import java.time.LocalDateTime 4 | import java.util.concurrent.TimeUnit 5 | import javax.inject.{Inject, Singleton} 6 | 7 | import me.yangbajing.ps.data.PsSqlDriver 8 | import me.yangbajing.ps.types.OwnerStatus.OwnerStatus 9 | import play.api.db.slick.DatabaseConfigProvider 10 | import play.api.libs.json.JsValue 11 | 12 | import scala.concurrent.Await 13 | import scala.concurrent.duration.{Duration, FiniteDuration} 14 | 15 | @Singleton 16 | class Schemas @Inject()(databaseConfigProvider: DatabaseConfigProvider) /* extends DatabaseModule*/ { 17 | val databaseConfig = databaseConfigProvider.get[PsSqlDriver] 18 | val driver: PsSqlDriver = databaseConfig.driver 19 | 20 | import driver.api._ 21 | 22 | def exec[R](action: DBIOAction[R, NoStream, Nothing])(implicit duration: FiniteDuration = Duration(10, TimeUnit.SECONDS)) = { 23 | Await.result(databaseConfig.db.run(action), duration) 24 | } 25 | 26 | def run[R](action: DBIOAction[R, NoStream, Nothing]) = { 27 | databaseConfig.db.run(action) 28 | } 29 | 30 | class TableUser(tag: Tag) extends Table[User](tag, "t_user") { 31 | val id = column[Long]("id", O.PrimaryKey) 32 | val attrs = column[JsValue]("attrs") 33 | val status = column[OwnerStatus]("status") 34 | val created_at = column[LocalDateTime]("created_at") 35 | 36 | def * = (id, attrs, status, created_at) <> (User.tupled, User.unapply) 37 | } 38 | 39 | class TableCredential(tag: Tag) extends Table[Credential](tag, "t_credential") { 40 | val id = column[Long]("id", O.PrimaryKey, O.AutoInc) 41 | val email = column[Option[String]]("email") 42 | val phone = column[Option[String]]("phone") 43 | val salt = column[Array[Byte]]("salt") 44 | val password = column[Array[Byte]]("password") 45 | val created_at = column[LocalDateTime]("created_at") 46 | 47 | def * = (id.?, email, phone, salt, password, created_at) <> (Credential.tupled, Credential.unapply) 48 | } 49 | 50 | def tCredential = TableQuery[TableCredential] 51 | 52 | def tUser = TableQuery[TableUser] 53 | 54 | def schema = 55 | tCredential.schema ++ 56 | tUser.schema 57 | 58 | } 59 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/app/views/_layout.scala.html: -------------------------------------------------------------------------------- 1 | @(title: String, 2 | head: Html = Html(""), 3 | foot: Html = Html(""), 4 | keywords: String = "play,scala,slick,slick-pg,postgresql,akka", 5 | description: String = "play seed")(body: Html) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | @title 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | @head 38 | 42 | 43 | 44 | @body 45 | @foot 46 | 47 | 48 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/app/utils/Filters.scala: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import javax.inject.Inject 4 | 5 | import akka.stream.Materializer 6 | import com.typesafe.scalalogging.StrictLogging 7 | import controllers.WebTools 8 | import me.yangbajing.ps.setting.SettingComponent 9 | import me.yangbajing.ps.util.Constants 10 | import play.api.http.HttpFilters 11 | import play.api.mvc.{EssentialFilter, Filter, RequestHeader, Result} 12 | 13 | import scala.concurrent.{ExecutionContext, Future} 14 | 15 | class Filters @Inject()(authFilter: FilterAfterForToken) extends HttpFilters { 16 | override def filters: Seq[EssentialFilter] = 17 | Seq(authFilter) 18 | } 19 | 20 | class FilterAfterForToken @Inject()(webTools: WebTools, 21 | settingComponent: SettingComponent, 22 | implicit val mat: Materializer, 23 | implicit val ec: ExecutionContext 24 | ) extends Filter with StrictLogging { 25 | val matchAll = settingComponent.filterAfterToken.matchAll 26 | val paths = settingComponent.filterAfterToken.paths 27 | val excludePaths = settingComponent.filterAfterToken.excludePaths 28 | 29 | override def apply(nextFilter: (RequestHeader) => Future[Result])(rh: RequestHeader): Future[Result] = { 30 | nextFilter(rh).flatMap { result => 31 | logger.debug("current path: " + rh.path) 32 | rh.path match { 33 | case path if !excludePaths.contains(path) && paths.exists(apiPath => path.startsWith(apiPath)) => 34 | val tokenOpt = webTools.getUserToken(rh) 35 | logger.debug("before tokenFilter: " + tokenOpt) 36 | 37 | if (matchAll) { 38 | if (tokenOpt.isEmpty) 39 | throw new IllegalAccessException("token not exists") 40 | } 41 | 42 | Future { 43 | tokenOpt 44 | .map { 45 | case Right(token) => 46 | // XXX 重设token 47 | result.withCookies(webTools.createCookie(Constants.PS_TOKEN_OWNER, token.toValue)) 48 | 49 | case Left(key) => 50 | result.withCookies(webTools.discardingSession(key)) 51 | } 52 | .getOrElse(result) 53 | } 54 | 55 | case _ => 56 | Future.successful(result) 57 | } 58 | } 59 | } 60 | 61 | } 62 | 63 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_progress-bars.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Progress bars 3 | // -------------------------------------------------- 4 | 5 | 6 | // Bar animations 7 | // ------------------------- 8 | 9 | // WebKit 10 | @-webkit-keyframes progress-bar-stripes { 11 | from { background-position: 40px 0; } 12 | to { background-position: 0 0; } 13 | } 14 | 15 | // Spec and IE10+ 16 | @keyframes progress-bar-stripes { 17 | from { background-position: 40px 0; } 18 | to { background-position: 0 0; } 19 | } 20 | 21 | 22 | // Bar itself 23 | // ------------------------- 24 | 25 | // Outer container 26 | .progress { 27 | overflow: hidden; 28 | height: $line-height-computed; 29 | margin-bottom: $line-height-computed; 30 | background-color: $progress-bg; 31 | border-radius: $progress-border-radius; 32 | @include box-shadow(inset 0 1px 2px rgba(0,0,0,.1)); 33 | } 34 | 35 | // Bar of progress 36 | .progress-bar { 37 | float: left; 38 | width: 0%; 39 | height: 100%; 40 | font-size: $font-size-small; 41 | line-height: $line-height-computed; 42 | color: $progress-bar-color; 43 | text-align: center; 44 | background-color: $progress-bar-bg; 45 | @include box-shadow(inset 0 -1px 0 rgba(0,0,0,.15)); 46 | @include transition(width .6s ease); 47 | } 48 | 49 | // Striped bars 50 | // 51 | // `.progress-striped .progress-bar` is deprecated as of v3.2.0 in favor of the 52 | // `.progress-bar-striped` class, which you just add to an existing 53 | // `.progress-bar`. 54 | .progress-striped .progress-bar, 55 | .progress-bar-striped { 56 | @include gradient-striped; 57 | background-size: 40px 40px; 58 | } 59 | 60 | // Call animation for the active one 61 | // 62 | // `.progress.active .progress-bar` is deprecated as of v3.2.0 in favor of the 63 | // `.progress-bar.active` approach. 64 | .progress.active .progress-bar, 65 | .progress-bar.active { 66 | @include animation(progress-bar-stripes 2s linear infinite); 67 | } 68 | 69 | 70 | // Variations 71 | // ------------------------- 72 | 73 | .progress-bar-success { 74 | @include progress-bar-variant($progress-bar-success-bg); 75 | } 76 | 77 | .progress-bar-info { 78 | @include progress-bar-variant($progress-bar-info-bg); 79 | } 80 | 81 | .progress-bar-warning { 82 | @include progress-bar-variant($progress-bar-warning-bg); 83 | } 84 | 85 | .progress-bar-danger { 86 | @include progress-bar-variant($progress-bar-danger-bg); 87 | } 88 | -------------------------------------------------------------------------------- /slick-postgresql-redis/project/BuildSettings.scala: -------------------------------------------------------------------------------- 1 | import Dependencies._ 2 | import sbt.Keys._ 3 | import sbt._ 4 | 5 | object BuildSettings { 6 | 7 | val DependsConfigure = "compile->compile;test->test" 8 | 9 | def basicSettings = Seq( 10 | version := "2.0.0", 11 | homepage := Some(new URL("https://github.com/yangbajing/play-seed")), 12 | organization := "me.yangbajing", 13 | organizationHomepage := Some(new URL("http://www.yangbajing.me")), 14 | startYear := Some(2015), 15 | scalaVersion := "2.11.8", 16 | scalacOptions := Seq( 17 | "-encoding", "utf8", 18 | //"-Ylog-classpath", 19 | "-feature", 20 | "-unchecked", 21 | "-deprecation"/*, 22 | "-explaintypes", 23 | "-Yno-adapted-args", 24 | "-Ywarn-dead-code", 25 | "-Ywarn-unused"*/ 26 | ), 27 | javacOptions := Seq( 28 | "-encoding", "utf8", 29 | "-deprecation", 30 | "-Xlint:unchecked", 31 | "-Xlint:deprecation" 32 | ), 33 | libraryDependencies ++= ( 34 | __compile(_slf4j) ++ 35 | __compile(_logback) ++ 36 | __compile(_typesafeConfig) ++ 37 | __compile(_scalaLogging) ++ 38 | __compile(_scala) ++ 39 | __test(_playTest) ++ 40 | __test(_scalatestplusPlay) ++ 41 | __test(_scalatest)), 42 | libraryDependencies ~= { 43 | _ map { 44 | case m if m.organization == "com.typesafe.play" => 45 | m.exclude("org.scala-lang", "scala-library"). 46 | // exclude("commons-logging", "commons-logging"). 47 | // exclude("com.google.guava", "guava"). 48 | // exclude("org.asynchttpclient", "async-http-client"). 49 | // exclude("com.typesafe.akka", "akka-actor"). 50 | // exclude("com.typesafe.akka", "akka-slf4j"). 51 | // exclude("org.scala-lang", "scala-compiler"). 52 | // exclude("org.scala-lang", "scala-reflect"). 53 | exclude("org.scala-lang.modules", "scala-xml"). 54 | exclude("org.scala-lang.modules", "scala-parser-combinators"). 55 | excludeAll() 56 | case m => m 57 | } 58 | }, 59 | sources in(Compile, doc) := Seq.empty, 60 | publishArtifact in(Compile, packageDoc) := false, 61 | publishArtifact in packageDoc := false, 62 | fork := true 63 | ) 64 | 65 | lazy val noPublishing = Seq( 66 | publish := (), 67 | publishLocal := () 68 | ) 69 | 70 | } 71 | 72 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_pagination.scss: -------------------------------------------------------------------------------- 1 | // 2 | // Pagination (multiple pages) 3 | // -------------------------------------------------- 4 | .pagination { 5 | display: inline-block; 6 | padding-left: 0; 7 | margin: $line-height-computed 0; 8 | border-radius: $border-radius-base; 9 | 10 | > li { 11 | display: inline; // Remove list-style and block-level defaults 12 | > a, 13 | > span { 14 | position: relative; 15 | float: left; // Collapse white-space 16 | padding: $padding-base-vertical $padding-base-horizontal; 17 | line-height: $line-height-base; 18 | text-decoration: none; 19 | color: $pagination-color; 20 | background-color: $pagination-bg; 21 | border: 1px solid $pagination-border; 22 | margin-left: -1px; 23 | } 24 | &:first-child { 25 | > a, 26 | > span { 27 | margin-left: 0; 28 | @include border-left-radius($border-radius-base); 29 | } 30 | } 31 | &:last-child { 32 | > a, 33 | > span { 34 | @include border-right-radius($border-radius-base); 35 | } 36 | } 37 | } 38 | 39 | > li > a, 40 | > li > span { 41 | &:hover, 42 | &:focus { 43 | color: $pagination-hover-color; 44 | background-color: $pagination-hover-bg; 45 | border-color: $pagination-hover-border; 46 | } 47 | } 48 | 49 | > .active > a, 50 | > .active > span { 51 | &, 52 | &:hover, 53 | &:focus { 54 | z-index: 2; 55 | color: $pagination-active-color; 56 | background-color: $pagination-active-bg; 57 | border-color: $pagination-active-border; 58 | cursor: default; 59 | } 60 | } 61 | 62 | > .disabled { 63 | > span, 64 | > span:hover, 65 | > span:focus, 66 | > a, 67 | > a:hover, 68 | > a:focus { 69 | color: $pagination-disabled-color; 70 | background-color: $pagination-disabled-bg; 71 | border-color: $pagination-disabled-border; 72 | cursor: $cursor-disabled; 73 | } 74 | } 75 | } 76 | 77 | // Sizing 78 | // -------------------------------------------------- 79 | 80 | // Large 81 | .pagination-lg { 82 | @include pagination-size($padding-large-vertical, $padding-large-horizontal, $font-size-large, $border-radius-large); 83 | } 84 | 85 | // Small 86 | .pagination-sm { 87 | @include pagination-size($padding-small-vertical, $padding-small-horizontal, $font-size-small, $border-radius-small); 88 | } 89 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-data/src/main/scala/me/yangbajing/ps/data/repo/UserRepo.scala: -------------------------------------------------------------------------------- 1 | package me.yangbajing.ps.data.repo 2 | 3 | import java.time.LocalDateTime 4 | import javax.inject.Inject 5 | 6 | import me.yangbajing.ps.data.record.{Credential, Schemas, User} 7 | import me.yangbajing.ps.types.OwnerStatus 8 | import me.yangbajing.ps.util.Utils 9 | import play.api.libs.json.{JsObject, JsValue} 10 | 11 | import scala.concurrent.{ExecutionContext, Future} 12 | 13 | /** 14 | * User数据库访问 15 | * Created by jingyang on 15/7/16. 16 | */ 17 | object UserRepo { 18 | type UserInfo = (User, Option[String], Option[String]) 19 | } 20 | 21 | class UserRepo @Inject()(schemas: Schemas)(implicit ec: ExecutionContext) { 22 | 23 | import schemas.driver.api._ 24 | import schemas._ 25 | 26 | import UserRepo.UserInfo 27 | 28 | def findOneById(id: Long): Future[Option[UserInfo]] = { 29 | val q = for { 30 | c <- tCredential if c.id === id 31 | u <- tUser if u.id === c.id 32 | } yield (u, c.email, c.phone) 33 | run(q.result).map(_.headOption) 34 | } 35 | 36 | def update(id: Long, attrs: JsValue): Future[Int] = { 37 | val action = tUser.filter(_.id === id).map(_.attrs).update(attrs).transactionally 38 | run(action) 39 | } 40 | 41 | def register(email: Option[String], phone: Option[String], pwd: String): Future[UserInfo] = { 42 | require(email.isDefined || phone.isDefined, "email和phone至少指定一个") 43 | 44 | val (salt, password) = Utils.generatePassword(pwd) 45 | val c = Credential(None, email, phone, salt, password, LocalDateTime.now()) 46 | val u = for { 47 | cid <- (tCredential returning tCredential.map(_.id)) += c 48 | ret <- tUser += User(cid, JsObject(Nil), OwnerStatus.ACTIVE, c.created_at) 49 | } yield (User(cid, JsObject(Nil), OwnerStatus.ACTIVE, c.created_at), email, phone) 50 | run(u.transactionally) 51 | } 52 | 53 | def signinByEmail(email: String, pwd: String) = { 54 | signinBy(Some(email), None, pwd) 55 | } 56 | 57 | def signinByPhone(phone: String, pwd: String) = { 58 | signinBy(None, Some(phone), pwd) 59 | } 60 | 61 | private def signinBy(email: Option[String], phone: Option[String], pwd: String): Future[Option[UserInfo]] = { 62 | require(email.isDefined || phone.isDefined, "email和phone至少指定一个") 63 | 64 | val q = for { 65 | c <- tCredential if c.email === email || c.phone === phone 66 | u <- tUser if u.id === c.id 67 | } yield (u, c) 68 | 69 | run(q.result).map { 70 | case (u, c) +: _ => 71 | require(java.util.Arrays.equals(Utils.generatePassword(c.salt, pwd), c.password), "密码不匹配") 72 | Some((u, c.email, c.phone)) 73 | 74 | case _ => 75 | None 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/app/controllers/WebTools.scala: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import java.util.concurrent.TimeUnit 4 | import javax.inject.{Inject, Singleton} 5 | 6 | import me.yangbajing.ps.business.service.CacheService 7 | import me.yangbajing.ps.data.domain.OwnerToken 8 | import me.yangbajing.ps.util.Constants 9 | import org.apache.commons.codec.digest.DigestUtils 10 | import play.api.libs.concurrent.Execution.Implicits.defaultContext 11 | import play.api.mvc.{Cookie, DiscardingCookie, RequestHeader} 12 | 13 | import scala.concurrent.Future 14 | import scala.concurrent.duration.Duration 15 | import scala.util.Random 16 | 17 | /** 18 | * cache放到redis里。 19 | */ 20 | @Singleton 21 | class WebTools @Inject()(cacheService: CacheService) { 22 | /** 23 | * 24 | * @param request 25 | * @return 26 | * case Some(_) => 27 | * future map { 28 | * case Right(_) => 成功获取到OwnerToken 29 | * case Left(key) => key无效 30 | * } 31 | * case None => token key's value 未找到 32 | */ 33 | def getUserToken(implicit request: RequestHeader): Option[Either[String, OwnerToken]] = { 34 | val str = request.cookies.find(_.name == Constants.PS_TOKEN_OWNER).map(_.value) orElse 35 | request.headers.get(Constants.PS_TOKEN_OWNER) orElse 36 | request.getQueryString(Constants.PS_TOKEN_OWNER) 37 | 38 | str.map { key => 39 | cacheService.getString(Constants.CACHE_KEY_TOKEN_PREFIX + key) match { 40 | case Some(value) => { 41 | val vc = value.split(Constants.SPLIT_CHAR).toVector 42 | val token = OwnerToken(vc(0).toLong, vecGet(vc, 1), vecGet(vc, 2), vecGet(vc, 3)) 43 | Right(token) 44 | } 45 | case None => 46 | Left(key) 47 | } 48 | } 49 | } 50 | 51 | @inline 52 | private def vecGet(vc: Vector[String], idx: Int): Option[String] = { 53 | if (vc.length > idx) Some(vc(idx)) else None 54 | } 55 | 56 | def createCookie(name: String, value: String): Cookie = { 57 | Cookie(name, value, Some(Constants.COOKIE_MAX_AGE), httpOnly = true) 58 | } 59 | 60 | def createSession(ownerToken: OwnerToken) = { 61 | val key = ownerToken.ownerId.toString + Constants.SPLIT_CHAR + 62 | DigestUtils.sha1Hex(Random.nextInt() + Random.nextString(12) + (System.currentTimeMillis() / 1000)) 63 | 64 | // XXX 设置缓存最长时间为一天,登录Session状态满一天后强制退出 65 | cacheService.set(Constants.CACHE_KEY_TOKEN_PREFIX + key, ownerToken.toValue, Duration(1, TimeUnit.DAYS)) 66 | 67 | createCookie(Constants.PS_TOKEN_OWNER, key) 68 | } 69 | 70 | def discardingSession(key: String = null): Cookie = { 71 | if (key == null) { 72 | cacheService.remove(key) 73 | } 74 | DiscardingCookie(Constants.PS_TOKEN_OWNER).toCookie 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_grid-framework.scss: -------------------------------------------------------------------------------- 1 | // Framework grid generation 2 | // 3 | // Used only by Bootstrap to generate the correct number of grid classes given 4 | // any value of `$grid-columns`. 5 | 6 | // [converter] This is defined recursively in LESS, but Sass supports real loops 7 | @mixin make-grid-columns($i: 1, $list: ".col-xs-#{$i}, .col-sm-#{$i}, .col-md-#{$i}, .col-lg-#{$i}") { 8 | @for $i from (1 + 1) through $grid-columns { 9 | $list: "#{$list}, .col-xs-#{$i}, .col-sm-#{$i}, .col-md-#{$i}, .col-lg-#{$i}"; 10 | } 11 | #{$list} { 12 | position: relative; 13 | // Prevent columns from collapsing when empty 14 | min-height: 1px; 15 | // Inner gutter via padding 16 | padding-left: ($grid-gutter-width / 2); 17 | padding-right: ($grid-gutter-width / 2); 18 | } 19 | } 20 | 21 | 22 | // [converter] This is defined recursively in LESS, but Sass supports real loops 23 | @mixin float-grid-columns($class, $i: 1, $list: ".col-#{$class}-#{$i}") { 24 | @for $i from (1 + 1) through $grid-columns { 25 | $list: "#{$list}, .col-#{$class}-#{$i}"; 26 | } 27 | #{$list} { 28 | float: left; 29 | } 30 | } 31 | 32 | 33 | @mixin calc-grid-column($index, $class, $type) { 34 | @if ($type == width) and ($index > 0) { 35 | .col-#{$class}-#{$index} { 36 | width: percentage(($index / $grid-columns)); 37 | } 38 | } 39 | @if ($type == push) and ($index > 0) { 40 | .col-#{$class}-push-#{$index} { 41 | left: percentage(($index / $grid-columns)); 42 | } 43 | } 44 | @if ($type == push) and ($index == 0) { 45 | .col-#{$class}-push-0 { 46 | left: auto; 47 | } 48 | } 49 | @if ($type == pull) and ($index > 0) { 50 | .col-#{$class}-pull-#{$index} { 51 | right: percentage(($index / $grid-columns)); 52 | } 53 | } 54 | @if ($type == pull) and ($index == 0) { 55 | .col-#{$class}-pull-0 { 56 | right: auto; 57 | } 58 | } 59 | @if ($type == offset) { 60 | .col-#{$class}-offset-#{$index} { 61 | margin-left: percentage(($index / $grid-columns)); 62 | } 63 | } 64 | } 65 | 66 | // [converter] This is defined recursively in LESS, but Sass supports real loops 67 | @mixin loop-grid-columns($columns, $class, $type) { 68 | @for $i from 0 through $columns { 69 | @include calc-grid-column($i, $class, $type); 70 | } 71 | } 72 | 73 | 74 | // Create grid for specific class 75 | @mixin make-grid($class) { 76 | @include float-grid-columns($class); 77 | @include loop-grid-columns($grid-columns, $class, width); 78 | @include loop-grid-columns($grid-columns, $class, pull); 79 | @include loop-grid-columns($grid-columns, $class, push); 80 | @include loop-grid-columns($grid-columns, $class, offset); 81 | } 82 | -------------------------------------------------------------------------------- /slick-postgresql-redis/project/Build.scala: -------------------------------------------------------------------------------- 1 | import _root_.sbt.Keys._ 2 | import _root_.sbt._ 3 | import play.sbt.Play.autoImport._ 4 | import play.sbt.PlayScala 5 | import play.twirl.sbt.Import.TwirlKeys 6 | 7 | object Build extends Build { 8 | 9 | import BuildSettings._ 10 | import Dependencies._ 11 | 12 | override lazy val settings = super.settings :+ { 13 | shellPrompt := (s => Project.extract(s).currentProject.id + " > ") 14 | } 15 | 16 | /////////////////////////////////////////////////////////////// 17 | // playSeed project 18 | /////////////////////////////////////////////////////////////// 19 | lazy val playSeed = Project("play-seed-root", file(".")) 20 | .aggregate(psWeb, psBusiness, psData, psUtil) 21 | 22 | /////////////////////////////////////////////////////////////// 23 | // projects 24 | /////////////////////////////////////////////////////////////// 25 | lazy val psWeb = Project("ps-web", file("ps-web")) 26 | .enablePlugins(PlayScala) 27 | .dependsOn(psBusiness, psData) 28 | .settings(basicSettings: _*) 29 | .settings( 30 | TwirlKeys.templateImports ++= Seq("me.yangbajing.ps.data.record._", "me.yangbajing.ps.data.domain._"), 31 | PlayKeys.playDefaultPort := 58082, 32 | libraryDependencies ++= ( 33 | __compile(_scalaModules) ++ 34 | __compile(_scalaLogging) ++ 35 | __compile(json) ++ 36 | __compile(ws) ++ 37 | __compile(_slf4j) ++ 38 | __compile(_logback)) 39 | ) 40 | 41 | lazy val psBusiness = Project("ps-business", file("ps-business")) 42 | .dependsOn(psData, psUtil % DependsConfigure) 43 | .settings(basicSettings: _*) 44 | .settings( 45 | libraryDependencies ++= ( 46 | __provided(_play) ++ 47 | __compile(_redisClient))) 48 | 49 | lazy val psData = Project("ps-data", file("ps-data")) 50 | .dependsOn(psUtil % DependsConfigure) 51 | .settings(basicSettings: _*) 52 | .settings( 53 | libraryDependencies ++= ( 54 | __provided(json) ++ 55 | __provided(ws) ++ 56 | __provided(_play) ++ 57 | __compile(_postgresql) ++ 58 | __compile(_hikariCP) ++ 59 | __compile(_slick) ++ 60 | __compile(_slickPg) ++ 61 | __compile(_playSlick))) 62 | 63 | lazy val psUtil = Project("ps-util", file("ps-util")) 64 | .settings(basicSettings: _*) 65 | .settings( 66 | libraryDependencies ++= ( 67 | __provided(_commonCodecs) ++ 68 | __provided(_commonsLang3) ++ 69 | __provided(json) ++ 70 | __provided(ws) ++ 71 | __provided(_play) ++ 72 | __compile(_bouncycastle) ++ 73 | __compile(_akkaActor) ++ 74 | __compile(_akkaSlf4j))) 75 | 76 | } 77 | 78 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/_print.scss: -------------------------------------------------------------------------------- 1 | /*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */ 2 | 3 | // ========================================================================== 4 | // Print styles. 5 | // Inlined to avoid the additional HTTP request: h5bp.com/r 6 | // ========================================================================== 7 | 8 | @media print { 9 | *, 10 | *:before, 11 | *:after { 12 | background: transparent !important; 13 | color: #000 !important; // Black prints faster: h5bp.com/s 14 | box-shadow: none !important; 15 | text-shadow: none !important; 16 | } 17 | 18 | a, 19 | a:visited { 20 | text-decoration: underline; 21 | } 22 | 23 | a[href]:after { 24 | content: " (" attr(href) ")"; 25 | } 26 | 27 | abbr[title]:after { 28 | content: " (" attr(title) ")"; 29 | } 30 | 31 | // Don't show links that are fragment identifiers, 32 | // or use the `javascript:` pseudo protocol 33 | a[href^="#"]:after, 34 | a[href^="javascript:"]:after { 35 | content: ""; 36 | } 37 | 38 | pre, 39 | blockquote { 40 | border: 1px solid #999; 41 | page-break-inside: avoid; 42 | } 43 | 44 | thead { 45 | display: table-header-group; // h5bp.com/t 46 | } 47 | 48 | tr, 49 | img { 50 | page-break-inside: avoid; 51 | } 52 | 53 | img { 54 | max-width: 100% !important; 55 | } 56 | 57 | p, 58 | h2, 59 | h3 { 60 | orphans: 3; 61 | widows: 3; 62 | } 63 | 64 | h2, 65 | h3 { 66 | page-break-after: avoid; 67 | } 68 | 69 | // Bootstrap specific changes start 70 | // 71 | // Chrome (OSX) fix for https://github.com/twbs/bootstrap/issues/11245 72 | // Once fixed, we can just straight up remove this. 73 | select { 74 | background: #fff !important; 75 | } 76 | 77 | // Bootstrap components 78 | .navbar { 79 | display: none; 80 | } 81 | .btn, 82 | .dropup > .btn { 83 | > .caret { 84 | border-top-color: #000 !important; 85 | } 86 | } 87 | .label { 88 | border: 1px solid #000; 89 | } 90 | 91 | .table { 92 | border-collapse: collapse !important; 93 | 94 | td, 95 | th { 96 | background-color: #fff !important; 97 | } 98 | } 99 | .table-bordered { 100 | th, 101 | td { 102 | border: 1px solid #ddd !important; 103 | } 104 | } 105 | 106 | // Bootstrap specific changes end 107 | } 108 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/conf/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ${user.dir}/logs/application.log 6 | 7 | 8 | ${user.dir}/logs/application-log-%d{yyyy-MM-dd}.gz 9 | 10 | 30 11 | 12 | 13 | %date{yyyy-MM-dd HH:mm:ss ZZZZ} [%level] from %logger in %thread - %message%n%xException 14 | 15 | 16 | 17 | 18 | ${user.dir}/logs/access.log 19 | 20 | 21 | ${user.dir}/logs/access-log-%d{yyyy-MM-dd}.gz 22 | 23 | 7 24 | 25 | 26 | %date{yyyy-MM-dd HH:mm:ss ZZZZ} %message 27 | 28 | false 29 | 30 | 31 | 32 | 33 | 34 | %date{HH:mm:ss.SSS} %coloredLevel %logger{30} - %message%n%xException{10} 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /slick-postgresql-redis/ps-web/static/src/site/scss/bootstrap/mixins/_forms.scss: -------------------------------------------------------------------------------- 1 | // Form validation states 2 | // 3 | // Used in forms.less to generate the form validation CSS for warnings, errors, 4 | // and successes. 5 | 6 | @mixin form-control-validation($text-color: #555, $border-color: #ccc, $background-color: #f5f5f5) { 7 | // Color the label and help text 8 | .help-block, 9 | .control-label, 10 | .radio, 11 | .checkbox, 12 | .radio-inline, 13 | .checkbox-inline, 14 | &.radio label, 15 | &.checkbox label, 16 | &.radio-inline label, 17 | &.checkbox-inline label { 18 | color: $text-color; 19 | } 20 | // Set the border and box shadow on specific inputs to match 21 | .form-control { 22 | border-color: $border-color; 23 | @include box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work 24 | &:focus { 25 | border-color: darken($border-color, 10%); 26 | $shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten($border-color, 20%); 27 | @include box-shadow($shadow); 28 | } 29 | } 30 | // Set validation states also for addons 31 | .input-group-addon { 32 | color: $text-color; 33 | border-color: $border-color; 34 | background-color: $background-color; 35 | } 36 | // Optional feedback icon 37 | .form-control-feedback { 38 | color: $text-color; 39 | } 40 | } 41 | 42 | 43 | // Form control focus state 44 | // 45 | // Generate a customized focus state and for any input with the specified color, 46 | // which defaults to the `$input-border-focus` variable. 47 | // 48 | // We highly encourage you to not customize the default value, but instead use 49 | // this to tweak colors on an as-needed basis. This aesthetic change is based on 50 | // WebKit's default styles, but applicable to a wider range of browsers. Its 51 | // usability and accessibility should be taken into account with any change. 52 | // 53 | // Example usage: change the default blue border and shadow to white for better 54 | // contrast against a dark gray background. 55 | @mixin form-control-focus($color: $input-border-focus) { 56 | $color-rgba: rgba(red($color), green($color), blue($color), .6); 57 | &:focus { 58 | border-color: $color; 59 | outline: 0; 60 | @include box-shadow(inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px $color-rgba); 61 | } 62 | } 63 | 64 | // Form control sizing 65 | // 66 | // Relative text size, padding, and border-radii changes for form controls. For 67 | // horizontal sizing, wrap controls in the predefined grid classes. `