├── .gitignore ├── .rubocop.yml ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── app.json ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ └── .keep │ ├── javascripts │ │ ├── application.js │ │ ├── cable.js │ │ └── channels │ │ │ └── .keep │ └── stylesheets │ │ ├── application.scss │ │ └── pay.scss ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ ├── home_controller.rb │ ├── pay_controller.rb │ ├── users │ │ ├── omniauth_callbacks_controller.rb │ │ ├── registrations_controller.rb │ │ └── sessions_controller.rb │ └── wechats_controller.rb ├── helpers │ ├── application_helper.rb │ └── pay_helper.rb ├── jobs │ └── application_job.rb ├── mailers │ ├── .keep │ └── application_mailer.rb ├── models │ ├── .keep │ ├── application_record.rb │ ├── concerns │ │ └── .keep │ └── user.rb └── views │ ├── devise │ ├── registrations │ │ └── new.html.erb │ └── sessions │ │ └── new.html.erb │ ├── home │ └── index.html.erb │ └── layouts │ ├── application.html.erb │ ├── mailer.html.erb │ └── mailer.text.erb ├── bin ├── bundle ├── rails ├── rake ├── setup ├── update └── yarn ├── config.ru ├── config ├── application.rb ├── application.yml.sample ├── boot.rb ├── cable.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── content_security_policy.rb │ ├── cookies_serializer.rb │ ├── devise.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── new_framework_defaults_5_2.rb │ ├── permissions_policy.rb │ ├── session_store.rb │ ├── sprockets.rb │ ├── wrap_parameters.rb │ └── wx_pay.rb ├── locales │ ├── devise.en.yml │ └── en.yml ├── menu.yaml.example ├── menu_development.yaml ├── menu_production.yaml ├── puma.rb ├── routes.rb ├── secrets.yml ├── spring.rb ├── storage.yml └── wechat.yml ├── db ├── migrate │ ├── 20160202145635_devise_create_users.rb │ ├── 20160202145932_add_omniauth_to_users.rb │ ├── 20160202145952_remove_email_uniq_index_from_users.rb │ ├── 20160202150536_add_user_detail_to_users.rb │ ├── 20160223133841_add_nickname_unique_index_to_users.rb │ ├── 20160224054132_add_birthday_to_users.rb │ └── 20180514231720_create_active_storage_tables.active_storage.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep ├── devise_failure.rb └── tasks │ ├── .keep │ └── wechat.rake ├── log └── .keep ├── package.json ├── public ├── 404.html ├── 422.html ├── 500.html ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon.ico └── robots.txt ├── test ├── application_system_test_case.rb ├── controllers │ ├── .keep │ ├── home_controller_test.rb │ └── pay_controller_test.rb ├── fixtures │ ├── .keep │ ├── files │ │ └── .keep │ └── users.yml ├── helpers │ └── .keep ├── integration │ └── .keep ├── mailers │ └── .keep ├── models │ ├── .keep │ └── user_test.rb ├── system │ └── .keep └── test_helper.rb ├── tmp └── .keep ├── vendor ├── .keep └── assets │ ├── javascripts │ └── .keep │ └── stylesheets │ └── .keep └── wechat-starter.sublime-project /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/* 12 | /tmp/* 13 | !/log/.keep 14 | !/tmp/.keep 15 | 16 | /node_modules 17 | /yarn-error.log 18 | 19 | .byebug_history 20 | 21 | # Ignore application configuration 22 | /config/application.yml.* 23 | /config/application.yml 24 | 25 | *.sublime-workspace 26 | /shared 27 | /public/MP*.txt 28 | config/menu.yaml 29 | config/master.key 30 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | Exclude: 3 | - bin/* 4 | - db/**/* 5 | 6 | Rails: 7 | Enabled: true 8 | 9 | ClassAndModuleChildren: 10 | Enabled: false 11 | 12 | ClassLength: 13 | Enabled: false 14 | 15 | Documentation: 16 | Enabled: false 17 | 18 | Metrics/LineLength: 19 | Max: 150 20 | 21 | Metrics/AbcSize: 22 | Max: 50 23 | 24 | Metrics/ClassLength: 25 | Max: 150 26 | 27 | Metrics/MethodLength: 28 | Max: 20 29 | 30 | Style/IndentationWidth: 31 | Width: 2 32 | 33 | Style/UnneededPercentQ: 34 | Enabled: false 35 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | env: 4 | global: 5 | - NOKOGIRI_USE_SYSTEM_LIBRARIES=true 6 | 7 | sudo: false 8 | 9 | addons: 10 | postgresql: "9.4" 11 | 12 | services: 13 | - postgresql 14 | 15 | rvm: 16 | - 2.4.1 17 | 18 | bundler_args: --without development --deployment --jobs=3 --retry=3 19 | 20 | cache: bundler 21 | 22 | before_script: 23 | - psql -c 'create database wechat_starter_test;' -U postgres 24 | - cp config/application.yml.sample config/application.yml 25 | - bundle exec rake db:migrate --trace 26 | 27 | script: bundle exec rake 28 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby '~> 3.0' 4 | 5 | git_source(:github) do |repo_name| 6 | repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") 7 | "https://github.com/#{repo_name}.git" 8 | end 9 | 10 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 11 | gem 'rails', '~> 7.1.0' 12 | # Use postgresql as the database for Active Record 13 | gem 'pg' 14 | # Use Puma as the app server 15 | gem 'puma', '~> 5.6' 16 | # Use SCSS for stylesheets 17 | gem 'sass-rails', '~> 6.0' 18 | # Use Uglifier as compressor for JavaScript assets 19 | gem 'terser', '~> 1.1' 20 | # See https://github.com/rails/execjs#readme for more supported runtimes 21 | # gem 'therubyracer', platforms: :ruby 22 | 23 | # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks 24 | gem 'turbolinks', '~> 5.2' 25 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 26 | gem 'jbuilder', '~> 2.11' 27 | # Use Redis adapter to run Action Cable in production 28 | # gem 'redis', '~> 3.0' 29 | # Use ActiveModel has_secure_password 30 | # gem 'bcrypt', '~> 3.1.7' 31 | 32 | # Use Capistrano for deployment 33 | # gem 'capistrano-rails', group: :development 34 | 35 | # Boot app faster 36 | gem "bootsnap", ">= 1.16.0", require: false 37 | 38 | group :development, :test do 39 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 40 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] 41 | # Adds support for Capybara system testing and selenium driver 42 | gem 'capybara', '>= 3.39' 43 | gem 'selenium-webdriver', '>= 4.11.0' 44 | end 45 | 46 | group :development do 47 | # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. 48 | gem 'web-console', '>= 4.2.0' 49 | gem 'listen', '>= 3.8.0' 50 | end 51 | 52 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 53 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 54 | 55 | gem 'pry', group: :development 56 | 57 | gem 'figaro' 58 | gem 'devise', '>= 4.9.2' 59 | gem 'wechat', '>= 0.17.1' 60 | gem 'wx_pay' 61 | gem 'weui-rails' 62 | gem 'omniauth-wechat-oauth2' 63 | gem 'omniauth', '~> 1.9' 64 | gem 'jquery-rails', '>= 4.6.0' 65 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (7.1.5) 5 | actionpack (= 7.1.5) 6 | activesupport (= 7.1.5) 7 | nio4r (~> 2.0) 8 | websocket-driver (>= 0.6.1) 9 | zeitwerk (~> 2.6) 10 | actionmailbox (7.1.5) 11 | actionpack (= 7.1.5) 12 | activejob (= 7.1.5) 13 | activerecord (= 7.1.5) 14 | activestorage (= 7.1.5) 15 | activesupport (= 7.1.5) 16 | mail (>= 2.7.1) 17 | net-imap 18 | net-pop 19 | net-smtp 20 | actionmailer (7.1.5) 21 | actionpack (= 7.1.5) 22 | actionview (= 7.1.5) 23 | activejob (= 7.1.5) 24 | activesupport (= 7.1.5) 25 | mail (~> 2.5, >= 2.5.4) 26 | net-imap 27 | net-pop 28 | net-smtp 29 | rails-dom-testing (~> 2.2) 30 | actionpack (7.1.5) 31 | actionview (= 7.1.5) 32 | activesupport (= 7.1.5) 33 | nokogiri (>= 1.8.5) 34 | racc 35 | rack (>= 2.2.4) 36 | rack-session (>= 1.0.1) 37 | rack-test (>= 0.6.3) 38 | rails-dom-testing (~> 2.2) 39 | rails-html-sanitizer (~> 1.6) 40 | actiontext (7.1.5) 41 | actionpack (= 7.1.5) 42 | activerecord (= 7.1.5) 43 | activestorage (= 7.1.5) 44 | activesupport (= 7.1.5) 45 | globalid (>= 0.6.0) 46 | nokogiri (>= 1.8.5) 47 | actionview (7.1.5) 48 | activesupport (= 7.1.5) 49 | builder (~> 3.1) 50 | erubi (~> 1.11) 51 | rails-dom-testing (~> 2.2) 52 | rails-html-sanitizer (~> 1.6) 53 | activejob (7.1.5) 54 | activesupport (= 7.1.5) 55 | globalid (>= 0.3.6) 56 | activemodel (7.1.5) 57 | activesupport (= 7.1.5) 58 | activerecord (7.1.5) 59 | activemodel (= 7.1.5) 60 | activesupport (= 7.1.5) 61 | timeout (>= 0.4.0) 62 | activestorage (7.1.5) 63 | actionpack (= 7.1.5) 64 | activejob (= 7.1.5) 65 | activerecord (= 7.1.5) 66 | activesupport (= 7.1.5) 67 | marcel (~> 1.0) 68 | activesupport (7.1.5) 69 | base64 70 | benchmark (>= 0.3) 71 | bigdecimal 72 | concurrent-ruby (~> 1.0, >= 1.0.2) 73 | connection_pool (>= 2.2.5) 74 | drb 75 | i18n (>= 1.6, < 2) 76 | logger (>= 1.4.2) 77 | minitest (>= 5.1) 78 | mutex_m 79 | securerandom (>= 0.3) 80 | tzinfo (~> 2.0) 81 | addressable (2.8.7) 82 | public_suffix (>= 2.0.2, < 7.0) 83 | autoprefixer-rails (10.4.19.0) 84 | execjs (~> 2) 85 | base64 (0.2.0) 86 | bcrypt (3.1.20) 87 | benchmark (0.4.0) 88 | bigdecimal (3.1.8) 89 | bindex (0.8.1) 90 | bootsnap (1.18.4) 91 | msgpack (~> 1.2) 92 | builder (3.3.0) 93 | byebug (11.1.3) 94 | capybara (3.40.0) 95 | addressable 96 | matrix 97 | mini_mime (>= 0.1.3) 98 | nokogiri (~> 1.11) 99 | rack (>= 1.6.0) 100 | rack-test (>= 0.6.3) 101 | regexp_parser (>= 1.5, < 3.0) 102 | xpath (~> 3.2) 103 | coderay (1.1.3) 104 | concurrent-ruby (1.3.4) 105 | connection_pool (2.4.1) 106 | crass (1.0.6) 107 | date (3.4.0) 108 | devise (4.9.4) 109 | bcrypt (~> 3.0) 110 | orm_adapter (~> 0.1) 111 | railties (>= 4.1.0) 112 | responders 113 | warden (~> 1.2.3) 114 | domain_name (0.6.20240107) 115 | drb (2.2.1) 116 | erubi (1.13.0) 117 | execjs (2.10.0) 118 | faraday (2.12.1) 119 | faraday-net_http (>= 2.0, < 3.5) 120 | json 121 | logger 122 | faraday-net_http (3.4.0) 123 | net-http (>= 0.5.0) 124 | ffi (1.17.0-aarch64-linux-gnu) 125 | ffi (1.17.0-arm-linux-gnu) 126 | ffi (1.17.0-arm64-darwin) 127 | ffi (1.17.0-x86-linux-gnu) 128 | ffi (1.17.0-x86_64-darwin) 129 | ffi (1.17.0-x86_64-linux-gnu) 130 | ffi-compiler (1.3.2) 131 | ffi (>= 1.15.5) 132 | rake 133 | figaro (1.2.0) 134 | thor (>= 0.14.0, < 2) 135 | globalid (1.2.1) 136 | activesupport (>= 6.1) 137 | hashie (5.0.0) 138 | http (5.2.0) 139 | addressable (~> 2.8) 140 | base64 (~> 0.1) 141 | http-cookie (~> 1.0) 142 | http-form_data (~> 2.2) 143 | llhttp-ffi (~> 0.5.0) 144 | http-accept (1.7.0) 145 | http-cookie (1.0.7) 146 | domain_name (~> 0.5) 147 | http-form_data (2.3.0) 148 | i18n (1.14.6) 149 | concurrent-ruby (~> 1.0) 150 | io-console (0.7.2) 151 | irb (1.14.1) 152 | rdoc (>= 4.0.0) 153 | reline (>= 0.4.2) 154 | jbuilder (2.13.0) 155 | actionview (>= 5.0.0) 156 | activesupport (>= 5.0.0) 157 | jquery-rails (4.6.0) 158 | rails-dom-testing (>= 1, < 3) 159 | railties (>= 4.2.0) 160 | thor (>= 0.14, < 2.0) 161 | json (2.8.2) 162 | jwt (2.9.3) 163 | base64 164 | listen (3.9.0) 165 | rb-fsevent (~> 0.10, >= 0.10.3) 166 | rb-inotify (~> 0.9, >= 0.9.10) 167 | llhttp-ffi (0.5.0) 168 | ffi-compiler (~> 1.0) 169 | rake (~> 13.0) 170 | logger (1.6.1) 171 | loofah (2.23.1) 172 | crass (~> 1.0.2) 173 | nokogiri (>= 1.12.0) 174 | mail (2.8.1) 175 | mini_mime (>= 0.1.1) 176 | net-imap 177 | net-pop 178 | net-smtp 179 | marcel (1.0.4) 180 | matrix (0.4.2) 181 | method_source (1.1.0) 182 | mime-types (3.6.0) 183 | logger 184 | mime-types-data (~> 3.2015) 185 | mime-types-data (3.2024.1105) 186 | mini_mime (1.1.5) 187 | minitest (5.25.2) 188 | msgpack (1.7.5) 189 | multi_xml (0.7.1) 190 | bigdecimal (~> 3.1) 191 | mutex_m (0.3.0) 192 | net-http (0.5.0) 193 | uri 194 | net-imap (0.5.1) 195 | date 196 | net-protocol 197 | net-pop (0.1.2) 198 | net-protocol 199 | net-protocol (0.2.2) 200 | timeout 201 | net-smtp (0.5.0) 202 | net-protocol 203 | netrc (0.11.0) 204 | nio4r (2.7.4) 205 | nokogiri (1.16.7-aarch64-linux) 206 | racc (~> 1.4) 207 | nokogiri (1.16.7-arm-linux) 208 | racc (~> 1.4) 209 | nokogiri (1.16.7-arm64-darwin) 210 | racc (~> 1.4) 211 | nokogiri (1.16.7-x86-linux) 212 | racc (~> 1.4) 213 | nokogiri (1.16.7-x86_64-darwin) 214 | racc (~> 1.4) 215 | nokogiri (1.16.7-x86_64-linux) 216 | racc (~> 1.4) 217 | oauth2 (2.0.9) 218 | faraday (>= 0.17.3, < 3.0) 219 | jwt (>= 1.0, < 3.0) 220 | multi_xml (~> 0.5) 221 | rack (>= 1.2, < 4) 222 | snaky_hash (~> 2.0) 223 | version_gem (~> 1.1) 224 | omniauth (1.9.2) 225 | hashie (>= 3.4.6) 226 | rack (>= 1.6.2, < 3) 227 | omniauth-oauth2 (1.7.3) 228 | oauth2 (>= 1.4, < 3) 229 | omniauth (>= 1.9, < 3) 230 | omniauth-wechat-oauth2 (0.2.3) 231 | omniauth-oauth2 (>= 1.7.3) 232 | orm_adapter (0.5.0) 233 | pg (1.5.9) 234 | pry (0.15.0) 235 | coderay (~> 1.1) 236 | method_source (~> 1.0) 237 | psych (5.2.0) 238 | stringio 239 | public_suffix (6.0.1) 240 | puma (5.6.9) 241 | nio4r (~> 2.0) 242 | racc (1.8.1) 243 | rack (2.2.10) 244 | rack-session (1.0.2) 245 | rack (< 3) 246 | rack-test (2.1.0) 247 | rack (>= 1.3) 248 | rackup (1.0.1) 249 | rack (< 3) 250 | webrick 251 | rails (7.1.5) 252 | actioncable (= 7.1.5) 253 | actionmailbox (= 7.1.5) 254 | actionmailer (= 7.1.5) 255 | actionpack (= 7.1.5) 256 | actiontext (= 7.1.5) 257 | actionview (= 7.1.5) 258 | activejob (= 7.1.5) 259 | activemodel (= 7.1.5) 260 | activerecord (= 7.1.5) 261 | activestorage (= 7.1.5) 262 | activesupport (= 7.1.5) 263 | bundler (>= 1.15.0) 264 | railties (= 7.1.5) 265 | rails-dom-testing (2.2.0) 266 | activesupport (>= 5.0.0) 267 | minitest 268 | nokogiri (>= 1.6) 269 | rails-html-sanitizer (1.6.0) 270 | loofah (~> 2.21) 271 | nokogiri (~> 1.14) 272 | railties (7.1.5) 273 | actionpack (= 7.1.5) 274 | activesupport (= 7.1.5) 275 | irb 276 | rackup (>= 1.0.0) 277 | rake (>= 12.2) 278 | thor (~> 1.0, >= 1.2.2) 279 | zeitwerk (~> 2.6) 280 | rake (13.2.1) 281 | rb-fsevent (0.11.2) 282 | rb-inotify (0.11.1) 283 | ffi (~> 1.0) 284 | rdoc (6.8.1) 285 | psych (>= 4.0.0) 286 | regexp_parser (2.9.2) 287 | reline (0.5.11) 288 | io-console (~> 0.5) 289 | responders (3.1.1) 290 | actionpack (>= 5.2) 291 | railties (>= 5.2) 292 | rest-client (2.1.0) 293 | http-accept (>= 1.7.0, < 2.0) 294 | http-cookie (>= 1.0.2, < 2.0) 295 | mime-types (>= 1.16, < 4.0) 296 | netrc (~> 0.8) 297 | rexml (3.3.9) 298 | rubyzip (2.3.2) 299 | sass (3.7.4) 300 | sass-listen (~> 4.0.0) 301 | sass-listen (4.0.0) 302 | rb-fsevent (~> 0.9, >= 0.9.4) 303 | rb-inotify (~> 0.9, >= 0.9.7) 304 | sass-rails (6.0.0) 305 | sassc-rails (~> 2.1, >= 2.1.1) 306 | sassc (2.4.0) 307 | ffi (~> 1.9) 308 | sassc-rails (2.1.2) 309 | railties (>= 4.0.0) 310 | sassc (>= 2.0) 311 | sprockets (> 3.0) 312 | sprockets-rails 313 | tilt 314 | securerandom (0.3.2) 315 | selenium-webdriver (4.26.0) 316 | base64 (~> 0.2) 317 | logger (~> 1.4) 318 | rexml (~> 3.2, >= 3.2.5) 319 | rubyzip (>= 1.2.2, < 3.0) 320 | websocket (~> 1.0) 321 | snaky_hash (2.0.1) 322 | hashie 323 | version_gem (~> 1.1, >= 1.1.1) 324 | sprockets (4.2.1) 325 | concurrent-ruby (~> 1.0) 326 | rack (>= 2.2.4, < 4) 327 | sprockets-rails (3.5.2) 328 | actionpack (>= 6.1) 329 | activesupport (>= 6.1) 330 | sprockets (>= 3.0.0) 331 | stringio (3.1.2) 332 | terser (1.2.4) 333 | execjs (>= 0.3.0, < 3) 334 | thor (1.3.2) 335 | tilt (2.4.0) 336 | timeout (0.4.2) 337 | turbolinks (5.2.1) 338 | turbolinks-source (~> 5.2) 339 | turbolinks-source (5.2.0) 340 | tzinfo (2.0.6) 341 | concurrent-ruby (~> 1.0) 342 | uri (1.0.2) 343 | version_gem (1.1.4) 344 | warden (1.2.9) 345 | rack (>= 2.0.9) 346 | web-console (4.2.1) 347 | actionview (>= 6.0.0) 348 | activemodel (>= 6.0.0) 349 | bindex (>= 0.4.0) 350 | railties (>= 6.0.0) 351 | webrick (1.9.0) 352 | websocket (1.2.11) 353 | websocket-driver (0.7.6) 354 | websocket-extensions (>= 0.1.0) 355 | websocket-extensions (0.1.5) 356 | wechat (0.17.5) 357 | activesupport (>= 6.0, < 8) 358 | http (>= 1.0.4, < 6) 359 | nokogiri (>= 1.6.0) 360 | rexml 361 | thor 362 | zeitwerk (~> 2.4) 363 | weui-rails (1.1.3) 364 | autoprefixer-rails (>= 5.2.1) 365 | sass (>= 3.3.4) 366 | wx_pay (0.21.0) 367 | activesupport (>= 3.2) 368 | rest-client (>= 2.0.0) 369 | xpath (3.2.0) 370 | nokogiri (~> 1.8) 371 | zeitwerk (2.7.1) 372 | 373 | PLATFORMS 374 | aarch64-linux 375 | arm-linux 376 | arm64-darwin-23 377 | x86-linux 378 | x86_64-darwin 379 | x86_64-linux 380 | 381 | DEPENDENCIES 382 | bootsnap (>= 1.16.0) 383 | byebug 384 | capybara (>= 3.39) 385 | devise (>= 4.9.2) 386 | figaro 387 | jbuilder (~> 2.11) 388 | jquery-rails (>= 4.6.0) 389 | listen (>= 3.8.0) 390 | omniauth (~> 1.9) 391 | omniauth-wechat-oauth2 392 | pg 393 | pry 394 | puma (~> 5.6) 395 | rails (~> 7.1.0) 396 | sass-rails (~> 6.0) 397 | selenium-webdriver (>= 4.11.0) 398 | terser (~> 1.1) 399 | turbolinks (~> 5.2) 400 | tzinfo-data 401 | web-console (>= 4.2.0) 402 | wechat (>= 0.17.1) 403 | weui-rails 404 | wx_pay 405 | 406 | RUBY VERSION 407 | ruby 3.2.2p53 408 | 409 | BUNDLED WITH 410 | 2.5.23 411 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wechat-starter 2 | 3 | [![Build Status][travis-badge]][travis] 4 | [![Code Climate](https://codeclimate.com/github/Eric-Guo/wechat-starter/badges/gpa.svg)](https://codeclimate.com/github/Eric-Guo/wechat-starter) 5 | 6 | Template for developing wechat in rails. 7 | 8 | ### Features 9 | 10 | * Process messages with [Eric-Guo/wechat](https://github.com/Eric-Guo/wechat) 11 | 12 | * UI library using [weui](https://github.com/weui/weui) with [weui-rails](https://github.com/Eric-Guo/weui-rails) 13 | 14 | * Pay with [jasl/wx_pay](https://github.com/jasl/wx_pay) 15 | 16 | * OAuth2 with [skinnyworm/omniauth-wechat-oauth2](https://github.com/skinnyworm/omniauth-wechat-oauth2) 17 | 18 | ### Screenshots 19 | 20 | 21 | 22 | 23 | 24 | ### Demo 25 | 26 | 27 | 28 | Service account sponsored by [Le Wagon Shanghai](https://www.lewagon.com/shanghai) 29 | 30 | # Install 31 | 32 | ### See wiki first 33 | 34 | * [Wiki](https://github.com/Eric-Guo/wechat-starter/wiki) 35 | 36 | ### Deploy to Heroku 37 | 38 | [![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy) 39 | 40 | ### Deploy to your own server 41 | 42 | * Requirements 43 | 44 | * PostgreSQL 45 | 46 | * Configure "服务器地址" as `http://your-domain.com/wechat` 47 | 48 | * Configure "测试授权目录" as `http://your-domain.com/` 49 | 50 | * Install 51 | 52 | * Create your new project and add this repo as template 53 | 54 | ``` bash 55 | mkdir your_app 56 | cd your_app 57 | git init 58 | git remote add template https://github.com/Eric-Guo/wechat-starter.git 59 | git fetch template 60 | git checkout -b master template/master 61 | bundle install 62 | rake db:create 63 | rake db:migrate 64 | ``` 65 | 66 | * Create `config/application.yml` and config following information: 67 | 68 | ``` yaml 69 | wechat_app_id: "AppID" 70 | wechat_secret: "AppSecret" 71 | wechat_token: "Token" 72 | wechat_encoding_aes_key: "EncodingAESKey" 73 | wechat_trusted_domain_fullname: "http://your_dev.proxy.qqbrowser.cc" 74 | 75 | # following is optional if you don't need wechat pay 76 | wechat_pay_mch_id: "merchant id" 77 | wechat_pay_api_key: "32 bits api key configured in pay.weixin.qq.com" 78 | wechat_pay_notify_url: "url to accept pay result notification" 79 | ``` 80 | 81 | * Start server 82 | 83 | `puma -C config/puma.rb` 84 | 85 | # Create customized menu 86 | 87 | It depends on `Rails.env` to choose the menu from config folder. 88 | 89 | For example, if `Rails.env == production`, then `config/menu_production.yaml` is chosed. 90 | 91 | ``` bash 92 | # create menu 93 | rake wechat:menu_create 94 | 95 | # show menu 96 | rake wechat:menu 97 | ``` 98 | 99 | # References 100 | 101 | * [omniauth with devise](https://github.com/plataformatec/devise/wiki/OmniAuth:-Overview) 102 | * [sorcery-weixin](https://github.com/goofansu/sorcery-weixin) 103 | 104 | # Extension projects 105 | 106 | * [rails authentication system](https://github.com/frankgu/rails_authentication_system) 107 | - wechat omniauth only login 108 | - requires phone and email to login the system 109 | - phone confirmation service using yunpian 110 | - I18n internationalization (default locale is zh-CN) 111 | - rpsec test and simplecov test coverage (97.2% LOC covered) 112 | 113 | # Contributors 114 | 115 | * [goofansu](https://github.com/goofansu) 116 | * [Eric-Guo](https://github.com/Eric-Guo) 117 | * [Frankgu](https://github.com/frankgu) 118 | 119 | [travis-badge]: https://travis-ci.org/Eric-Guo/wechat-starter.svg 120 | [travis]: https://travis-ci.org/Eric-Guo/wechat-starter 121 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wechat-starter demo", 3 | "repository": "https://github.com/Eric-Guo/wechat-starter", 4 | "addons": [ 5 | "heroku-postgresql:hobby-dev" 6 | ], 7 | "env": { 8 | "wechat_app_id": "公众号AppId", 9 | "wechat_secret": "公众号AppSecret", 10 | "wechat_token": "公众号Token", 11 | "wechat_encrypt_mode": "消息是否加密,是填true,否则填false", 12 | "wechat_encoding_aes_key": { 13 | "description": "公众号EncodingAESKey,测试号留空", 14 | "required": false 15 | }, 16 | "wechat_pay_mch_id": { 17 | "description": "商户号,在微信支付中查看,没有就留空", 18 | "required": false 19 | }, 20 | "wechat_pay_api_key": { 21 | "description": "微信支付的API KEY,没有就留空", 22 | "required": false 23 | }, 24 | "wechat_pay_notify_url": { 25 | "description": "http://your.app.domain/wx_notify", 26 | "required": false 27 | } 28 | }, 29 | "scripts": { 30 | "postdeploy": "bundle exec rake db:migrate" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's 5 | // vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require rails-ujs 15 | //= require_tree . 16 | -------------------------------------------------------------------------------- /app/assets/javascripts/cable.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command. 3 | // 4 | //= require actioncable 5 | //= require_self 6 | //= require_tree ./channels 7 | 8 | (function() { 9 | this.App || (this.App = {}); 10 | 11 | App.cable = ActionCable.createConsumer(); 12 | 13 | }).call(this); 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/channels/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/app/assets/javascripts/channels/.keep -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any styles 10 | * defined in the other CSS/SCSS files in this directory. It is generally better to create a new 11 | * file per style scope. 12 | * 13 | */ 14 | @import 'weui'; 15 | -------------------------------------------------------------------------------- /app/assets/stylesheets/pay.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the pay controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | end 4 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | before_action :authenticate_user! 3 | 4 | # to allow using wechat, wechat_oauth2 in controller and wechat_config_js in view 5 | wechat_api 6 | 7 | def index 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/pay_controller.rb: -------------------------------------------------------------------------------- 1 | class PayController < ApplicationController 2 | before_action :authenticate_user!, except: [:wx_notify] 3 | skip_before_action :verify_authenticity_token, only: [:wx_notify] 4 | 5 | def wx_pay 6 | pay_params = { 7 | body: 'Test Wechat Pay', 8 | out_trade_no: "trade-#{Time.now.to_i}", 9 | total_fee: 1, 10 | spbill_create_ip: request.remote_ip, 11 | notify_url: Figaro.env.wechat_pay_notify_url, 12 | trade_type: 'JSAPI', 13 | openid: current_user.uid 14 | } 15 | 16 | prepay_result = WxPay::Service.invoke_unifiedorder(pay_params) 17 | if prepay_result.success? 18 | js_pay_params = { 19 | prepayid: prepay_result['prepay_id'], 20 | noncestr: prepay_result['nonce_str'] 21 | } 22 | pay_params = WxPay::Service.generate_js_pay_req js_pay_params 23 | logger.info pay_params 24 | render json: pay_params 25 | else 26 | logger.error prepay_result['return_msg'] 27 | render json: prepay_result 28 | end 29 | end 30 | 31 | def wx_notify 32 | result = Hash.from_xml(request.body.read)['xml'] 33 | logger.info result.inspect 34 | if WxPay::Sign.verify?(result) 35 | render xml: { return_code: 'SUCCESS', return_msg: 'OK' }.to_xml(root: 'xml', dasherize: false) 36 | else 37 | render xml: { return_code: 'FAIL', return_msg: 'Signature Error' }.to_xml(root: 'xml', dasherize: false) 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/controllers/users/omniauth_callbacks_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController 2 | def wechat 3 | @user = User.from_omniauth(request.env['omniauth.auth']) 4 | if @user.persisted? 5 | sign_in_and_redirect @user, event: :authentication 6 | set_flash_message(:notice, :success, kind: 'wechat') if is_navigational_format? 7 | else 8 | session['devise.wechat_data'] = request.env['omniauth.auth'] 9 | redirect_to new_user_registration_url 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/controllers/users/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::RegistrationsController < Devise::RegistrationsController 2 | before_action :configure_sign_up_params, only: [:create] 3 | # before_action :configure_account_update_params, only: [:update] 4 | 5 | wechat_api 6 | 7 | # GET /resource/sign_up 8 | # def new 9 | # super 10 | # end 11 | 12 | # POST /resource 13 | def create 14 | super 15 | end 16 | 17 | # GET /resource/edit 18 | # def edit 19 | # super 20 | # end 21 | 22 | # PUT /resource 23 | # def update 24 | # super 25 | # end 26 | 27 | # DELETE /resource 28 | # def destroy 29 | # super 30 | # end 31 | 32 | # GET /resource/cancel 33 | # Forces the session data which is usually expired after sign 34 | # in to be expired now. This is useful if the user wants to 35 | # cancel oauth signing in/up in the middle of the process, 36 | # removing all OAuth session data. 37 | # def cancel 38 | # super 39 | # end 40 | 41 | # protected 42 | 43 | # If you have extra params to permit, append them to the sanitizer. 44 | def configure_sign_up_params 45 | devise_parameter_sanitizer.permit(:sign_up, keys: [:birthday]) 46 | end 47 | 48 | # If you have extra params to permit, append them to the sanitizer. 49 | # def configure_account_update_params 50 | # devise_parameter_sanitizer.for(:account_update) << :attribute 51 | # end 52 | 53 | # The path used after sign up. 54 | # def after_sign_up_path_for(resource) 55 | # super(resource) 56 | # end 57 | 58 | # The path used after sign up for inactive accounts. 59 | # def after_inactive_sign_up_path_for(resource) 60 | # super(resource) 61 | # end 62 | end 63 | -------------------------------------------------------------------------------- /app/controllers/users/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::SessionsController < Devise::SessionsController 2 | def new 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/wechats_controller.rb: -------------------------------------------------------------------------------- 1 | class WechatsController < ApplicationController 2 | # to allow using wechat message DSL and web page helper 3 | wechat_responder 4 | 5 | # default text responder when no other match 6 | on :text do |request, content| 7 | request.reply.text "echo: #{content}" # Just echo 8 | end 9 | 10 | # When receive 'help', will trigger this responder 11 | on :text, with: 'help' do |request| 12 | request.reply.text 'help content' 13 | end 14 | 15 | # When receive 'news', will match and will got count as as parameter 16 | on :text, with: /^(\d+) news$/ do |request, count| 17 | # Wechat article can only contain max 10 items, large than 10 will dropped. 18 | news = (1..count.to_i).each_with_object([]) { |n, memo| memo << { title: 'News title', content: "No. #{n} news content" } } 19 | request.reply.news(news) do |article, n, index| # article is return object 20 | article.item title: "#{index} #{n[:title]}", description: n[:content], pic_url: 'http://www.baidu.com/img/bdlogo.gif', url: 'http://www.baidu.com/' 21 | end 22 | end 23 | 24 | on :event, with: 'subscribe' do |request| 25 | request.reply.text "#{request[:FromUserName]} subscribe now" 26 | end 27 | 28 | # When unsubscribe user scan qrcode qrscene_xxxxxx to subscribe in public account 29 | # notice user will subscribe public account at same time, so wechat won't trigger subscribe event any more 30 | on :scan, with: 'qrscene_xxxxxx' do |request, ticket| 31 | request.reply.text "Unsubscribe user #{request[:FromUserName]} Ticket #{ticket}" 32 | end 33 | 34 | # When subscribe user scan scene_id in public account 35 | on :scan, with: 'scene_id' do |request, ticket| 36 | request.reply.text "Subscribe user #{request[:FromUserName]} Ticket #{ticket}" 37 | end 38 | 39 | # When no any on :scan responder can match subscribe user scaned scene_id 40 | on :event, with: 'scan' do |request| 41 | if request[:EventKey].present? 42 | request.reply.text "event scan got EventKey #{request[:EventKey]} Ticket #{request[:Ticket]}" 43 | end 44 | end 45 | 46 | # When enterprise user press menu BINDING_QR_CODE and success to scan bar code 47 | on :scan, with: 'BINDING_QR_CODE' do |request, scan_result, scan_type| 48 | request.reply.text "User #{request[:FromUserName]} ScanResult #{scan_result} ScanType #{scan_type}" 49 | end 50 | 51 | # Except QR code, wechat can also scan CODE_39 bar code in enterprise account 52 | on :scan, with: 'BINDING_BARCODE' do |message, scan_result| 53 | if scan_result.start_with? 'CODE_39,' 54 | message.reply.text "User: #{message[:FromUserName]} scan barcode, result is #{scan_result.split(',')[1]}" 55 | end 56 | end 57 | 58 | # When user click the menu button 59 | on :click, with: 'BOOK_LUNCH' do |request, key| 60 | request.reply.text "User: #{request[:FromUserName]} click #{key}" 61 | end 62 | 63 | # When user view URL in the menu button 64 | on :view, with: 'http://wechat.somewhere.com/view_url' do |request, view| 65 | request.reply.text "#{request[:FromUserName]} view #{view}" 66 | end 67 | 68 | # When user sent the imsage 69 | on :image do |request| 70 | request.reply.image(request[:MediaId]) # Echo the sent image to user 71 | end 72 | 73 | # When user sent the voice 74 | on :voice do |request| 75 | request.reply.voice(request[:MediaId]) # Echo the sent voice to user 76 | end 77 | 78 | # When user sent the video 79 | on :video do |request| 80 | nickname = wechat.user(request[:FromUserName])['nickname'] # Call wechat api to get sender nickname 81 | request.reply.video(request[:MediaId], title: 'Echo', description: "Got #{nickname} sent video") # Echo the sent video to user 82 | end 83 | 84 | # When user sent location 85 | on :location do |request| 86 | request.reply.text("Latitude: #{message[:Latitude]} Longitude: #{message[:Longitude]} Precision: #{message[:Precision]}") 87 | end 88 | 89 | on :event, with: 'unsubscribe' do |request| 90 | request.reply.success # user can not receive this message 91 | end 92 | 93 | # When user enter the app / agent app 94 | on :event, with: 'enter_agent' do |request| 95 | request.reply.text "#{request[:FromUserName]} enter agent app now" 96 | end 97 | 98 | # When batch job create/update user (incremental) finished. 99 | on :batch_job, with: 'sync_user' do |request, batch_job| 100 | request.reply.text "sync_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}" 101 | end 102 | 103 | # When batch job replace user (full sync) finished. 104 | on :batch_job, with: 'replace_user' do |request, batch_job| 105 | request.reply.text "replace_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}" 106 | end 107 | 108 | # When batch job invent user finished. 109 | on :batch_job, with: 'invite_user' do |request, batch_job| 110 | request.reply.text "invite_user job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}" 111 | end 112 | 113 | # When batch job replace department (full sync) finished. 114 | on :batch_job, with: 'replace_party' do |request, batch_job| 115 | request.reply.text "replace_party job #{batch_job[:JobId]} finished, return code #{batch_job[:ErrCode]}, return message #{batch_job[:ErrMsg]}" 116 | end 117 | 118 | on :event do |request| 119 | logger.info request.inspect 120 | if request[:CardId].present? 121 | request.reply.text "CardId: #{request[:CardId]}, Event: #{request[:Event]}" 122 | end 123 | end 124 | 125 | # Any not match above will fail to below 126 | on :fallback, respond: 'fallback message' 127 | end 128 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/pay_helper.rb: -------------------------------------------------------------------------------- 1 | module PayHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/app/mailers/.keep -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/app/models/.keep -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | # Include default devise modules. Others available are: 3 | # :confirmable, :lockable, :timeoutable and :omniauthable 4 | devise :database_authenticatable, :registerable, 5 | :recoverable, :rememberable, :trackable, :validatable, :omniauthable 6 | 7 | before_validation :set_password 8 | # validates :birthday, presence: true 9 | 10 | def self.from_omniauth(auth) 11 | where(provider: auth.provider, uid: auth.uid).first_or_initialize do |user| 12 | user.nickname = auth.info.nickname 13 | user.sex = auth.info.sex 14 | user.avatar = auth.info.headimgurl 15 | end 16 | end 17 | 18 | def self.new_with_session(params, session) 19 | super.tap do |user| 20 | if data = session['devise.wechat_data'] 21 | user.provider = data['provider'] 22 | user.uid = data['uid'] 23 | user.nickname = data['info']['nickname'] 24 | user.sex = data['info']['sex'] 25 | user.avatar = data['info']['headimgurl'] 26 | end 27 | end 28 | end 29 | 30 | def email_required? 31 | false 32 | end 33 | 34 | private 35 | def set_password 36 | self.password ||= Devise.friendly_token[0, 20] 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= wechat_config_js debug: false, api: %w(closeWindow) -%> 3 | 4 |
5 |
6 |

Sign Up

7 |
8 |
9 | 10 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> 11 |
Following fields is required
12 |
13 |
14 |
15 | <%= f.label :birthday, class: 'weui-label' %> 16 |
17 |
18 | <%= f.date_field :birthday, class: 'weui-input' %> 19 |
20 |
21 |
22 |
23 | <%= f.submit "Confirm", class: 'weui-btn weui-btn_primary' %> 24 | Close 25 |
26 | <% end %> 27 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |

请点击下方“使用完整服务”授权微信登录。

5 |
6 |
7 | -------------------------------------------------------------------------------- /app/views/home/index.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= wechat_config_js debug: true, api: %w(hideMenuItems closeWindow chooseWXPay) -%> 3 | 8 | 9 |
10 |
11 |

Hello, <%= current_user.nickname %>

12 | 13 |
14 |
15 |

16 | <% if Figaro.env.wechat_pay_mch_id %> 17 | Try wechat pay! 18 | <% end %> 19 | Close 20 |

21 |
22 |
23 | 24 | <% if Figaro.env.wechat_pay_mch_id %> 25 | 44 | <% end %> 45 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | WechatStarter 5 | 6 | 7 | <%= stylesheet_link_tag 'application', media: 'all' %> 8 | <%= javascript_include_tag 'application' %> 9 | <%= csrf_meta_tags %> 10 | 11 | 12 | <%= yield %> 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path("..", __dir__) 6 | 7 | def system!(*args) 8 | system(*args, exception: true) 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to set up or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts "== Installing dependencies ==" 17 | system! "gem install bundler --conservative" 18 | system("bundle check") || system!("bundle install") 19 | 20 | # puts "\n== Copying sample files ==" 21 | # unless File.exist?("config/database.yml") 22 | # FileUtils.cp "config/database.yml.sample", "config/database.yml" 23 | # end 24 | 25 | puts "\n== Preparing database ==" 26 | system! "bin/rails db:prepare" 27 | 28 | puts "\n== Removing old logs and tempfiles ==" 29 | system! "bin/rails log:clear tmp:clear" 30 | 31 | puts "\n== Restarting application server ==" 32 | system! "bin/rails restart" 33 | end 34 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'fileutils' 3 | include FileUtils 4 | 5 | # path to your application root. 6 | APP_ROOT = File.expand_path('..', __dir__) 7 | 8 | def system!(*args) 9 | system(*args) || abort("\n== Command #{args} failed ==") 10 | end 11 | 12 | chdir APP_ROOT do 13 | # This script is a way to update your development environment automatically. 14 | # Add necessary update steps to this file. 15 | 16 | puts '== Installing dependencies ==' 17 | system! 'gem install bundler --conservative' 18 | system('bundle check') || system!('bundle install') 19 | 20 | # Install JavaScript dependencies if using Yarn 21 | # system('bin/yarn') 22 | 23 | puts "\n== Updating database ==" 24 | system! 'bin/rails db:migrate' 25 | 26 | puts "\n== Removing old logs and tempfiles ==" 27 | system! 'bin/rails log:clear tmp:clear' 28 | 29 | puts "\n== Restarting application server ==" 30 | system! 'bin/rails restart' 31 | end 32 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_ROOT = File.expand_path('..', __dir__) 3 | Dir.chdir(APP_ROOT) do 4 | yarn = ENV["PATH"].split(File::PATH_SEPARATOR). 5 | select { |dir| File.expand_path(dir) != __dir__ }. 6 | product(["yarn", "yarn.cmd", "yarn.ps1"]). 7 | map { |dir, file| File.expand_path(file, dir) }. 8 | find { |file| File.executable?(file) } 9 | 10 | if yarn 11 | exec yarn, *ARGV 12 | else 13 | $stderr.puts "Yarn executable was not detected in the system." 14 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 15 | exit 1 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative "config/environment" 4 | 5 | run Rails.application 6 | Rails.application.load_server 7 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative "boot" 2 | 3 | require "rails/all" 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module WechatStarter 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 7.0 13 | 14 | # Please, add to the `ignore` list any other `lib` subdirectories that do 15 | # not contain `.rb` files, or that should not be reloaded or eager loaded. 16 | # Common ones are `templates`, `generators`, or `middleware`, for example. 17 | config.autoload_lib(ignore: %w(assets tasks)) 18 | 19 | # Configuration for the application, engines, and railties goes here. 20 | # 21 | # These settings can be overridden in specific environments using the files 22 | # in config/environments, which are processed later. 23 | # 24 | # config.time_zone = "Central Time (US & Canada)" 25 | # config.eager_load_paths << Rails.root.join("extras") 26 | config.autoload_paths << "#{Rails.root}/lib" 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /config/application.yml.sample: -------------------------------------------------------------------------------- 1 | # We just provide a minimal testing public account in order to let testing pass. 2 | wechat_app_id: "wx90b3e204fcb50e72" 3 | wechat_secret: "7153c8b6f0c2f5e5b6672033422ae3fb" 4 | wechat_token: "Token" 5 | wechat_encoding_aes_key: "EncodingAESKey" 6 | wechat_trusted_domain_fullname: "http://your_dev.proxy.qqbrowser.cc" 7 | 8 | # following is optional if you don't need wechat pay 9 | wechat_pay_mch_id: "merchant id" 10 | wechat_pay_api_key: "32 bits api key configured in pay.weixin.qq.com" 11 | wechat_pay_notify_url: "http://your_dev.proxy.qqbrowser.cc/wx_notify" -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 2 | 3 | require "bundler/setup" # Set up gems listed in the Gemfile. 4 | require "bootsnap/setup" # Speed up boot time by caching expensive operations. 5 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: wechat_starter_production 11 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # PostgreSQL. Versions 9.1 and up are supported. 2 | # 3 | # Install the pg driver: 4 | # gem install pg 5 | # On OS X with Homebrew: 6 | # gem install pg -- --with-pg-config=/usr/local/bin/pg_config 7 | # On OS X with MacPorts: 8 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config 9 | # On Windows: 10 | # gem install pg 11 | # Choose the win32 build. 12 | # Install PostgreSQL and put its /bin directory on your path. 13 | # 14 | # Configure Using Gemfile 15 | # gem 'pg' 16 | # 17 | default: &default 18 | adapter: postgresql 19 | encoding: unicode 20 | # For details on connection pooling, see Rails configuration guide 21 | # http://guides.rubyonrails.org/configuring.html#database-pooling 22 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 23 | 24 | development: 25 | <<: *default 26 | database: wechat_starter_development 27 | 28 | # The specified database role being used to connect to postgres. 29 | # To create additional roles in postgres see `$ createuser --help`. 30 | # When left blank, postgres will use the default role. This is 31 | # the same name as the operating system user that initialized the database. 32 | #username: wechat-starter 33 | 34 | # The password associated with the postgres role (username). 35 | #password: 36 | 37 | # Connect on a TCP socket. Omitted by default since the client uses a 38 | # domain socket that doesn't need configuration. Windows does not have 39 | # domain sockets, so uncomment these lines. 40 | #host: localhost 41 | 42 | # The TCP port the server listens on. Defaults to 5432. 43 | # If your server runs on a different port number, change accordingly. 44 | #port: 5432 45 | 46 | # Schema search path. The server defaults to $user,public 47 | #schema_search_path: myapp,sharedapp,public 48 | 49 | # Minimum log levels, in increasing order: 50 | # debug5, debug4, debug3, debug2, debug1, 51 | # log, notice, warning, error, fatal, and panic 52 | # Defaults to warning. 53 | #min_messages: notice 54 | 55 | # Warning: The database defined as "test" will be erased and 56 | # re-generated from your development database when you run "rake". 57 | # Do not set this db to the same as development or production. 58 | test: 59 | <<: *default 60 | database: wechat_starter_test 61 | 62 | # As with config/secrets.yml, you never want to store sensitive information, 63 | # like your database password, in your source code. If your source code is 64 | # ever seen by anyone, they now have access to your database. 65 | # 66 | # Instead, provide the password as a unix environment variable when you boot 67 | # the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database 68 | # for a full rundown on how to provide these environment variables in a 69 | # production deployment. 70 | # 71 | # On Heroku and other platform providers, you may have a full connection URL 72 | # available as an environment variable. For example: 73 | # 74 | # DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" 75 | # 76 | # You can use this database configuration with: 77 | # 78 | # production: 79 | # url: <%= ENV['DATABASE_URL'] %> 80 | # 81 | production: 82 | <<: *default 83 | database: wechat_starter_production 84 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # In the development environment your application's code is reloaded any time 7 | # it changes. This slows down response time but is perfect for development 8 | # since you don't have to restart the web server when you make code changes. 9 | config.enable_reloading = true 10 | 11 | # Do not eager load code on boot. 12 | config.eager_load = false 13 | 14 | # Show full error reports. 15 | config.consider_all_requests_local = true 16 | 17 | # Enable server timing 18 | config.server_timing = true 19 | 20 | # Enable/disable caching. By default caching is disabled. 21 | # Run rails dev:cache to toggle caching. 22 | if Rails.root.join("tmp/caching-dev.txt").exist? 23 | config.action_controller.perform_caching = true 24 | config.action_controller.enable_fragment_cache_logging = true 25 | 26 | config.cache_store = :memory_store 27 | config.public_file_server.headers = { 28 | "Cache-Control" => "public, max-age=#{2.days.to_i}" 29 | } 30 | else 31 | config.action_controller.perform_caching = false 32 | 33 | config.cache_store = :null_store 34 | end 35 | 36 | # Store uploaded files on the local file system (see config/storage.yml for options). 37 | config.active_storage.service = :local 38 | 39 | # Don't care if the mailer can't send. 40 | config.action_mailer.raise_delivery_errors = false 41 | 42 | config.action_mailer.perform_caching = false 43 | 44 | # Print deprecation notices to the Rails logger. 45 | config.active_support.deprecation = :log 46 | 47 | # Raise exceptions for disallowed deprecations. 48 | config.active_support.disallowed_deprecation = :raise 49 | 50 | # Tell Active Support which deprecation messages to disallow. 51 | config.active_support.disallowed_deprecation_warnings = [] 52 | 53 | # Raise an error on page load if there are pending migrations. 54 | config.active_record.migration_error = :page_load 55 | 56 | # Highlight code that triggered database queries in logs. 57 | config.active_record.verbose_query_logs = true 58 | 59 | # Highlight code that enqueued background job in logs. 60 | config.active_job.verbose_enqueue_logs = true 61 | 62 | # Suppress logger output for asset requests. 63 | config.assets.quiet = true 64 | 65 | # Raises error for missing translations. 66 | # config.i18n.raise_on_missing_translations = true 67 | 68 | # Annotate rendered view with file names. 69 | # config.action_view.annotate_rendered_view_with_filenames = true 70 | 71 | # Uncomment if you wish to allow Action Cable access from any origin. 72 | # config.action_cable.disable_request_forgery_protection = true 73 | 74 | # Raise error when a before_action's only/except options reference missing actions 75 | config.action_controller.raise_on_missing_callback_actions = true 76 | end 77 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # Code is not reloaded between requests. 7 | config.enable_reloading = false 8 | 9 | # Eager load code on boot. This eager loads most of Rails and 10 | # your application in memory, allowing both threaded web servers 11 | # and those relying on copy on write to perform better. 12 | # Rake tasks automatically ignore this option for performance. 13 | config.eager_load = true 14 | 15 | # Full error reports are disabled and caching is turned on. 16 | config.consider_all_requests_local = false 17 | config.action_controller.perform_caching = true 18 | 19 | # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment 20 | # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files). 21 | # config.require_master_key = true 22 | 23 | # Enable static file serving from the `/public` folder (turn off if using NGINX/Apache for it). 24 | config.public_file_server.enabled = true 25 | 26 | config.assets.js_compressor = :terser 27 | # Compress CSS using a preprocessor. 28 | # config.assets.css_compressor = :sass 29 | 30 | # Do not fallback to assets pipeline if a precompiled asset is missed. 31 | config.assets.compile = false 32 | 33 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 34 | # config.asset_host = "http://assets.example.com" 35 | 36 | # Specifies the header that your server uses for sending files. 37 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache 38 | # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX 39 | 40 | # Store uploaded files on the local file system (see config/storage.yml for options). 41 | config.active_storage.service = :local 42 | 43 | # Mount Action Cable outside main process or domain. 44 | # config.action_cable.mount_path = nil 45 | # config.action_cable.url = "wss://example.com/cable" 46 | # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ] 47 | 48 | # Assume all access to the app is happening through a SSL-terminating reverse proxy. 49 | # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies. 50 | # config.assume_ssl = true 51 | 52 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 53 | config.force_ssl = true 54 | 55 | # Log to STDOUT by default 56 | config.logger = ActiveSupport::Logger.new(STDOUT) 57 | .tap { |logger| logger.formatter = ::Logger::Formatter.new } 58 | .then { |logger| ActiveSupport::TaggedLogging.new(logger) } 59 | 60 | # Prepend all log lines with the following tags. 61 | config.log_tags = [ :request_id ] 62 | 63 | # Info include generic and useful information about system operation, but avoids logging too much 64 | # information to avoid inadvertent exposure of personally identifiable information (PII). If you 65 | # want to log everything, set the level to "debug". 66 | config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") 67 | 68 | # Use a different cache store in production. 69 | # config.cache_store = :mem_cache_store 70 | 71 | # Use a real queuing backend for Active Job (and separate queues per environment). 72 | # config.active_job.queue_adapter = :resque 73 | # config.active_job.queue_name_prefix = "wechat_starter_production" 74 | 75 | config.action_mailer.perform_caching = false 76 | 77 | # Ignore bad email addresses and do not raise email delivery errors. 78 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 79 | # config.action_mailer.raise_delivery_errors = false 80 | 81 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 82 | # the I18n.default_locale when a translation cannot be found). 83 | config.i18n.fallbacks = true 84 | 85 | # Don't log any deprecations. 86 | config.active_support.report_deprecations = false 87 | 88 | # Do not dump schema after migrations. 89 | config.active_record.dump_schema_after_migration = false 90 | 91 | # Enable DNS rebinding protection and other `Host` header attacks. 92 | # config.hosts = [ 93 | # "example.com", # Allow requests from example.com 94 | # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com` 95 | # ] 96 | # Skip DNS rebinding protection for the default health check endpoint. 97 | # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } 98 | end 99 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | # The test environment is used exclusively to run your application's 4 | # test suite. You never need to work with it otherwise. Remember that 5 | # your test database is "scratch space" for the test suite and is wiped 6 | # and recreated between test runs. Don't rely on the data there! 7 | 8 | Rails.application.configure do 9 | # Settings specified here will take precedence over those in config/application.rb. 10 | 11 | # While tests run files are not watched, reloading is not necessary. 12 | config.enable_reloading = false 13 | 14 | # Eager loading loads your entire application. When running a single test locally, 15 | # this is usually not necessary, and can slow down your test suite. However, it's 16 | # recommended that you enable it in continuous integration systems to ensure eager 17 | # loading is working properly before deploying your code. 18 | config.eager_load = ENV["CI"].present? 19 | 20 | # Configure public file server for tests with Cache-Control for performance. 21 | config.public_file_server.enabled = true 22 | config.public_file_server.headers = { 23 | "Cache-Control" => "public, max-age=#{1.hour.to_i}" 24 | } 25 | 26 | # Show full error reports and disable caching. 27 | config.consider_all_requests_local = true 28 | config.action_controller.perform_caching = false 29 | config.cache_store = :null_store 30 | 31 | # Raise exceptions instead of rendering exception templates. 32 | config.action_dispatch.show_exceptions = :rescuable 33 | 34 | # Disable request forgery protection in test environment. 35 | config.action_controller.allow_forgery_protection = false 36 | 37 | # Store uploaded files on the local file system in a temporary directory. 38 | config.active_storage.service = :test 39 | 40 | config.action_mailer.perform_caching = false 41 | 42 | # Tell Action Mailer not to deliver emails to the real world. 43 | # The :test delivery method accumulates sent emails in the 44 | # ActionMailer::Base.deliveries array. 45 | config.action_mailer.delivery_method = :test 46 | 47 | # Print deprecation notices to the stderr. 48 | config.active_support.deprecation = :stderr 49 | 50 | # Raise exceptions for disallowed deprecations. 51 | config.active_support.disallowed_deprecation = :raise 52 | 53 | # Tell Active Support which deprecation messages to disallow. 54 | config.active_support.disallowed_deprecation_warnings = [] 55 | 56 | # Raises error for missing translations. 57 | # config.i18n.raise_on_missing_translations = true 58 | 59 | # Annotate rendered view with file names. 60 | # config.action_view.annotate_rendered_view_with_filenames = true 61 | 62 | # Raise error when a before_action's only/except options reference missing actions 63 | config.action_controller.raise_on_missing_callback_actions = true 64 | end 65 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ActiveSupport::Reloader.to_prepare do 4 | # ApplicationController.renderer.defaults.merge!( 5 | # http_host: 'example.org', 6 | # https: false 7 | # ) 8 | # end 9 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = "1.0" 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in the app/assets 11 | # folder are already added. 12 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 13 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code 7 | # by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'". 8 | Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"] 9 | -------------------------------------------------------------------------------- /config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy. 4 | # See the Securing Rails Applications Guide for more information: 5 | # https://guides.rubyonrails.org/security.html#content-security-policy-header 6 | 7 | # Rails.application.configure do 8 | # config.content_security_policy do |policy| 9 | # policy.default_src :self, :https 10 | # policy.font_src :self, :https, :data 11 | # policy.img_src :self, :https, :data 12 | # policy.object_src :none 13 | # policy.script_src :self, :https 14 | # policy.style_src :self, :https 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | # 19 | # # Generate session nonces for permitted importmap, inline scripts, and inline styles. 20 | # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } 21 | # config.content_security_policy_nonce_directives = %w(script-src style-src) 22 | # 23 | # # Report violations without enforcing the policy. 24 | # # config.content_security_policy_report_only = true 25 | # end 26 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/devise.rb: -------------------------------------------------------------------------------- 1 | # Use this hook to configure devise mailer, warden hooks and so forth. 2 | # Many of these configuration options can be set straight in your model. 3 | Devise.setup do |config| 4 | # The secret key used by Devise. Devise uses this key to generate 5 | # random tokens. Changing this key will render invalid all existing 6 | # confirmation, reset password and unlock tokens in the database. 7 | # Devise will use the `secret_key_base` on Rails 4+ applications as its `secret_key` 8 | # by default. You can change it below and use your own secret key. 9 | # config.secret_key = '834a8fc598541e0d70ff2cfba939ad43ccf711276c8a24d02b2ced6ebad9617138808327324682c3c5b074c15e585db4182a90da3f0c8264a7b55cab3bf0141e' 10 | 11 | # ==> Mailer Configuration 12 | # Configure the e-mail address which will be shown in Devise::Mailer, 13 | # note that it will be overwritten if you use your own mailer class 14 | # with default "from" parameter. 15 | config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com' 16 | 17 | # Configure the class responsible to send e-mails. 18 | # config.mailer = 'Devise::Mailer' 19 | 20 | # ==> ORM configuration 21 | # Load and configure the ORM. Supports :active_record (default) and 22 | # :mongoid (bson_ext recommended) by default. Other ORMs may be 23 | # available as additional gems. 24 | require 'devise/orm/active_record' 25 | 26 | # ==> Configuration for any authentication mechanism 27 | # Configure which keys are used when authenticating a user. The default is 28 | # just :email. You can configure it to use [:username, :subdomain], so for 29 | # authenticating a user, both parameters are required. Remember that those 30 | # parameters are used only when authenticating and not when retrieving from 31 | # session. If you need permissions, you should implement that in a before filter. 32 | # You can also supply a hash where the value is a boolean determining whether 33 | # or not authentication should be aborted when the value is not present. 34 | # config.authentication_keys = [:email] 35 | 36 | # Configure parameters from the request object used for authentication. Each entry 37 | # given should be a request method and it will automatically be passed to the 38 | # find_for_authentication method and considered in your model lookup. For instance, 39 | # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. 40 | # The same considerations mentioned for authentication_keys also apply to request_keys. 41 | # config.request_keys = [] 42 | 43 | # Configure which authentication keys should be case-insensitive. 44 | # These keys will be downcased upon creating or modifying a user and when used 45 | # to authenticate or find a user. Default is :email. 46 | config.case_insensitive_keys = [:email] 47 | 48 | # Configure which authentication keys should have whitespace stripped. 49 | # These keys will have whitespace before and after removed upon creating or 50 | # modifying a user and when used to authenticate or find a user. Default is :email. 51 | config.strip_whitespace_keys = [:email] 52 | 53 | # Tell if authentication through request.params is enabled. True by default. 54 | # It can be set to an array that will enable params authentication only for the 55 | # given strategies, for example, `config.params_authenticatable = [:database]` will 56 | # enable it only for database (email + password) authentication. 57 | # config.params_authenticatable = true 58 | 59 | # Tell if authentication through HTTP Auth is enabled. False by default. 60 | # It can be set to an array that will enable http authentication only for the 61 | # given strategies, for example, `config.http_authenticatable = [:database]` will 62 | # enable it only for database authentication. The supported strategies are: 63 | # :database = Support basic authentication with authentication key + password 64 | # config.http_authenticatable = false 65 | 66 | # If 401 status code should be returned for AJAX requests. True by default. 67 | # config.http_authenticatable_on_xhr = true 68 | 69 | # The realm used in Http Basic Authentication. 'Application' by default. 70 | # config.http_authentication_realm = 'Application' 71 | 72 | # It will change confirmation, password recovery and other workflows 73 | # to behave the same regardless if the e-mail provided was right or wrong. 74 | # Does not affect registerable. 75 | # config.paranoid = true 76 | 77 | # By default Devise will store the user in session. You can skip storage for 78 | # particular strategies by setting this option. 79 | # Notice that if you are skipping storage for all authentication paths, you 80 | # may want to disable generating routes to Devise's sessions controller by 81 | # passing skip: :sessions to `devise_for` in your config/routes.rb 82 | config.skip_session_storage = [:http_auth] 83 | 84 | # By default, Devise cleans up the CSRF token on authentication to 85 | # avoid CSRF token fixation attacks. This means that, when using AJAX 86 | # requests for sign in and sign up, you need to get a new CSRF token 87 | # from the server. You can disable this option at your own risk. 88 | # config.clean_up_csrf_token_on_authentication = true 89 | 90 | # ==> Configuration for :database_authenticatable 91 | # For bcrypt, this is the cost for hashing the password and defaults to 10. If 92 | # using other encryptors, it sets how many times you want the password re-encrypted. 93 | # 94 | # Limiting the stretches to just one in testing will increase the performance of 95 | # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use 96 | # a value less than 10 in other environments. Note that, for bcrypt (the default 97 | # encryptor), the cost increases exponentially with the number of stretches (e.g. 98 | # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). 99 | config.stretches = Rails.env.test? ? 1 : 10 100 | 101 | # Setup a pepper to generate the encrypted password. 102 | # config.pepper = '7f91b0818bf4fe376be79fa39b63d7f61a2ba3f4e10bf4e39469f7cce0768d01b42da3c0670d36e8aae93cc52d98c01227ee7a42791387ba47c6c2f5e34961cc' 103 | 104 | # Send a notification email when the user's password is changed 105 | # config.send_password_change_notification = false 106 | 107 | # ==> Configuration for :confirmable 108 | # A period that the user is allowed to access the website even without 109 | # confirming their account. For instance, if set to 2.days, the user will be 110 | # able to access the website for two days without confirming their account, 111 | # access will be blocked just in the third day. Default is 0.days, meaning 112 | # the user cannot access the website without confirming their account. 113 | # config.allow_unconfirmed_access_for = 2.days 114 | 115 | # A period that the user is allowed to confirm their account before their 116 | # token becomes invalid. For example, if set to 3.days, the user can confirm 117 | # their account within 3 days after the mail was sent, but on the fourth day 118 | # their account can't be confirmed with the token any more. 119 | # Default is nil, meaning there is no restriction on how long a user can take 120 | # before confirming their account. 121 | # config.confirm_within = 3.days 122 | 123 | # If true, requires any email changes to be confirmed (exactly the same way as 124 | # initial account confirmation) to be applied. Requires additional unconfirmed_email 125 | # db field (see migrations). Until confirmed, new email is stored in 126 | # unconfirmed_email column, and copied to email column on successful confirmation. 127 | config.reconfirmable = true 128 | 129 | # Defines which key will be used when confirming an account 130 | # config.confirmation_keys = [:email] 131 | 132 | # ==> Configuration for :rememberable 133 | # The time the user will be remembered without asking for credentials again. 134 | # config.remember_for = 2.weeks 135 | 136 | # Invalidates all the remember me tokens when the user signs out. 137 | config.expire_all_remember_me_on_sign_out = true 138 | 139 | # If true, extends the user's remember period when remembered via cookie. 140 | # config.extend_remember_period = false 141 | 142 | # Options to be passed to the created cookie. For instance, you can set 143 | # secure: true in order to force SSL only cookies. 144 | # config.rememberable_options = {} 145 | 146 | # ==> Configuration for :validatable 147 | # Range for password length. 148 | config.password_length = 8..72 149 | 150 | # Email regex used to validate email formats. It simply asserts that 151 | # one (and only one) @ exists in the given string. This is mainly 152 | # to give user feedback and not to assert the e-mail validity. 153 | # config.email_regexp = /\A[^@]+@[^@]+\z/ 154 | 155 | # ==> Configuration for :timeoutable 156 | # The time you want to timeout the user session without activity. After this 157 | # time the user will be asked for credentials again. Default is 30 minutes. 158 | # config.timeout_in = 30.minutes 159 | 160 | # ==> Configuration for :lockable 161 | # Defines which strategy will be used to lock an account. 162 | # :failed_attempts = Locks an account after a number of failed attempts to sign in. 163 | # :none = No lock strategy. You should handle locking by yourself. 164 | # config.lock_strategy = :failed_attempts 165 | 166 | # Defines which key will be used when locking and unlocking an account 167 | # config.unlock_keys = [:email] 168 | 169 | # Defines which strategy will be used to unlock an account. 170 | # :email = Sends an unlock link to the user email 171 | # :time = Re-enables login after a certain amount of time (see :unlock_in below) 172 | # :both = Enables both strategies 173 | # :none = No unlock strategy. You should handle unlocking by yourself. 174 | # config.unlock_strategy = :both 175 | 176 | # Number of authentication tries before locking an account if lock_strategy 177 | # is failed attempts. 178 | # config.maximum_attempts = 20 179 | 180 | # Time interval to unlock the account if :time is enabled as unlock_strategy. 181 | # config.unlock_in = 1.hour 182 | 183 | # Warn on the last attempt before the account is locked. 184 | # config.last_attempt_warning = true 185 | 186 | # ==> Configuration for :recoverable 187 | # 188 | # Defines which key will be used when recovering the password for an account 189 | # config.reset_password_keys = [:email] 190 | 191 | # Time interval you can reset your password with a reset password key. 192 | # Don't put a too small interval or your users won't have the time to 193 | # change their passwords. 194 | config.reset_password_within = 6.hours 195 | 196 | # When set to false, does not sign a user in automatically after their password is 197 | # reset. Defaults to true, so a user is signed in automatically after a reset. 198 | # config.sign_in_after_reset_password = true 199 | 200 | # ==> Configuration for :encryptable 201 | # Allow you to use another encryption algorithm besides bcrypt (default). You can use 202 | # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1, 203 | # :authlogic_sha512 (then you should set stretches above to 20 for default behavior) 204 | # and :restful_authentication_sha1 (then you should set stretches to 10, and copy 205 | # REST_AUTH_SITE_KEY to pepper). 206 | # 207 | # Require the `devise-encryptable` gem when using anything other than bcrypt 208 | # config.encryptor = :sha512 209 | 210 | # ==> Scopes configuration 211 | # Turn scoped views on. Before rendering "sessions/new", it will first check for 212 | # "users/sessions/new". It's turned off by default because it's slower if you 213 | # are using only default views. 214 | # config.scoped_views = false 215 | 216 | # Configure the default scope given to Warden. By default it's the first 217 | # devise role declared in your routes (usually :user). 218 | # config.default_scope = :user 219 | 220 | # Set this configuration to false if you want /users/sign_out to sign out 221 | # only the current scope. By default, Devise signs out all scopes. 222 | # config.sign_out_all_scopes = true 223 | 224 | # ==> Navigation configuration 225 | # Lists the formats that should be treated as navigational. Formats like 226 | # :html, should redirect to the sign in page when the user does not have 227 | # access, but formats like :xml or :json, should return 401. 228 | # 229 | # If you have any extra navigational formats, like :iphone or :mobile, you 230 | # should add them to the navigational formats lists. 231 | # 232 | # The "*/*" below is required to match Internet Explorer requests. 233 | # config.navigational_formats = ['*/*', :html] 234 | 235 | # The default HTTP method used to sign out a resource. Default is :delete. 236 | config.sign_out_via = :delete 237 | 238 | # ==> OmniAuth 239 | # Add a new OmniAuth provider. Check the wiki for more information on setting 240 | # up on your models and hooks. 241 | # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' 242 | 243 | # ==> Warden configuration 244 | # If you want to use other strategies, that are not supported by Devise, or 245 | # change the failure app, you can configure them inside the config.warden block. 246 | # 247 | # config.warden do |manager| 248 | # manager.intercept_401 = false 249 | # manager.default_strategies(scope: :user).unshift :some_external_strategy 250 | # end 251 | 252 | # ==> Mountable engine configurations 253 | # When using Devise inside an engine, let's call it `MyEngine`, and this engine 254 | # is mountable, there are some extra configurations to be taken into account. 255 | # The following options are available, assuming the engine is mounted as: 256 | # 257 | # mount MyEngine, at: '/my_engine' 258 | # 259 | # The router that invoked `devise_for`, in the example above, would be: 260 | # config.router_name = :my_engine 261 | # 262 | # When using OmniAuth, Devise cannot automatically set OmniAuth path, 263 | # so you need to do it manually. For the users scope, it would be: 264 | # config.omniauth_path_prefix = '/my_engine/users/auth' 265 | config.omniauth :wechat, Figaro.env.wechat_app_id, Figaro.env.wechat_secret, 266 | client_options: { authorize_url: "https://open.weixin.qq.com/connect/oauth2/authorize#wechat_redirect" }, 267 | authorize_params: { scope: "snsapi_userinfo" } 268 | config.warden { |manager| manager.failure_app = DeviseFailure } 269 | end 270 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. 4 | # Use this to limit dissemination of sensitive information. 5 | # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. 6 | Rails.application.config.filter_parameters += [ 7 | :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn 8 | ] 9 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, "\\1en" 8 | # inflect.singular /^(ox)en/i, "\\1" 9 | # inflect.irregular "person", "people" 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym "RESTful" 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/new_framework_defaults_5_2.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.2 upgrade. 4 | # 5 | # Once upgraded flip defaults one by one to migrate to the new default. 6 | # 7 | # Read the Guide for Upgrading Ruby on Rails for more info on each option. 8 | 9 | # Make Active Record use stable #cache_key alongside new #cache_version method. 10 | # This is needed for recyclable cache keys. 11 | # Rails.application.config.active_record.cache_versioning = true 12 | 13 | # Use AES-256-GCM authenticated encryption for encrypted cookies. 14 | # Also, embed cookie expiry in signed or encrypted cookies for increased security. 15 | # 16 | # This option is not backwards compatible with earlier Rails versions. 17 | # It's best enabled when your entire app is migrated and stable on 5.2. 18 | # 19 | # Existing cookies will be converted on read then written with the new scheme. 20 | # Rails.application.config.action_dispatch.use_authenticated_cookie_encryption = true 21 | 22 | # Use AES-256-GCM authenticated encryption as default cipher for encrypting messages 23 | # instead of AES-256-CBC, when use_authenticated_message_encryption is set to true. 24 | # Rails.application.config.active_support.use_authenticated_message_encryption = true 25 | 26 | # Add default protection from forgery to ActionController::Base instead of in 27 | # ApplicationController. 28 | # Rails.application.config.action_controller.default_protect_from_forgery = true 29 | 30 | # Store boolean values are in sqlite3 databases as 1 and 0 instead of 't' and 31 | # 'f' after migrating old data. 32 | # Rails.application.config.active_record.sqlite3.represent_boolean_as_integer = true 33 | 34 | # Use SHA-1 instead of MD5 to generate non-sensitive digests, such as the ETag header. 35 | # Rails.application.config.active_support.use_sha1_digests = true 36 | -------------------------------------------------------------------------------- /config/initializers/permissions_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide HTTP permissions policy. For further 4 | # information see: https://developers.google.com/web/updates/2018/06/feature-policy 5 | 6 | # Rails.application.config.permissions_policy do |policy| 7 | # policy.camera :none 8 | # policy.gyroscope :none 9 | # policy.microphone :none 10 | # policy.usb :none 11 | # policy.fullscreen :self 12 | # policy.payment :self, "https://secure.example.com" 13 | # end 14 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_wechat_starter_session' 4 | -------------------------------------------------------------------------------- /config/initializers/sprockets.rb: -------------------------------------------------------------------------------- 1 | Sprockets.register_compressor 'application/javascript', :terser, Terser::Compressor 2 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/initializers/wx_pay.rb: -------------------------------------------------------------------------------- 1 | WxPay.appid = Figaro.env.wechat_app_id 2 | WxPay.mch_id = Figaro.env.wechat_pay_mch_id 3 | WxPay.key = Figaro.env.wechat_pay_api_key 4 | WxPay.debug_mode = true 5 | -------------------------------------------------------------------------------- /config/locales/devise.en.yml: -------------------------------------------------------------------------------- 1 | # Additional translations at https://github.com/plataformatec/devise/wiki/I18n 2 | 3 | en: 4 | devise: 5 | confirmations: 6 | confirmed: "Your email address has been successfully confirmed." 7 | send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." 8 | send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." 9 | failure: 10 | already_authenticated: "You are already signed in." 11 | inactive: "Your account is not activated yet." 12 | invalid: "Invalid %{authentication_keys} or password." 13 | locked: "Your account is locked." 14 | last_attempt: "You have one more attempt before your account is locked." 15 | not_found_in_database: "Invalid %{authentication_keys} or password." 16 | timeout: "Your session expired. Please sign in again to continue." 17 | unauthenticated: "You need to sign in or sign up before continuing." 18 | unconfirmed: "You have to confirm your email address before continuing." 19 | mailer: 20 | confirmation_instructions: 21 | subject: "Confirmation instructions" 22 | reset_password_instructions: 23 | subject: "Reset password instructions" 24 | unlock_instructions: 25 | subject: "Unlock instructions" 26 | password_change: 27 | subject: "Password Changed" 28 | omniauth_callbacks: 29 | failure: "Could not authenticate you from %{kind} because \"%{reason}\"." 30 | success: "Successfully authenticated from %{kind} account." 31 | passwords: 32 | no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." 33 | send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." 34 | send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." 35 | updated: "Your password has been changed successfully. You are now signed in." 36 | updated_not_active: "Your password has been changed successfully." 37 | registrations: 38 | destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." 39 | signed_up: "Welcome! You have signed up successfully." 40 | signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." 41 | signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." 42 | signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." 43 | update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." 44 | updated: "Your account has been updated successfully." 45 | sessions: 46 | signed_in: "Signed in successfully." 47 | signed_out: "Signed out successfully." 48 | already_signed_out: "Signed out successfully." 49 | unlocks: 50 | send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." 51 | send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." 52 | unlocked: "Your account has been unlocked successfully. Please sign in to continue." 53 | errors: 54 | messages: 55 | already_confirmed: "was already confirmed, please try signing in" 56 | confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" 57 | expired: "has expired, please request a new one" 58 | not_found: "not found" 59 | not_locked: "was not locked" 60 | not_saved: 61 | one: "1 error prohibited this %{resource} from being saved:" 62 | other: "%{count} errors prohibited this %{resource} from being saved:" 63 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at https://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /config/menu.yaml.example: -------------------------------------------------------------------------------- 1 | # example from Eric-Guo/wechat 2 | button: 3 | - 4 | name: "Want" 5 | sub_button: 6 | - 7 | type: "scancode_waitmsg" 8 | name: "绑定用餐二维码" 9 | key: "BINDING_QR_CODE" 10 | - 11 | type: "click" 12 | name: "预订午餐" 13 | key: "BOOK_LUNCH" 14 | - 15 | type: "click" 16 | name: "预订晚餐" 17 | key: "BOOK_DINNER" 18 | - 19 | name: "Query" 20 | sub_button: 21 | - 22 | type: "click" 23 | name: "进出记录" 24 | key: "BADGE_IN_OUT" 25 | - 26 | type: "click" 27 | name: "年假余额" 28 | key: "ANNUAL_LEAVE" 29 | - 30 | type: "view" 31 | name: "About" 32 | url: "http://blog.cloud-mes.com/" 33 | -------------------------------------------------------------------------------- /config/menu_development.yaml: -------------------------------------------------------------------------------- 1 | button: 2 | - 3 | type: "view" 4 | name: "wechat-starter" 5 | url: "http://wechat-starter.herokuapp.com" 6 | -------------------------------------------------------------------------------- /config/menu_production.yaml: -------------------------------------------------------------------------------- 1 | button: 2 | - 3 | type: "view" 4 | name: "wechat-starter" 5 | url: "http://wechat-starter.ggrok.com" 6 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } 9 | threads min_threads_count, max_threads_count 10 | 11 | # Specifies the `worker_timeout` threshold that Puma will use to wait before 12 | # terminating a worker in development environments. 13 | # 14 | worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" 15 | 16 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 17 | # 18 | port ENV.fetch("PORT") { 3000 } 19 | 20 | # Specifies the `environment` that Puma will run in. 21 | # 22 | environment ENV.fetch("RAILS_ENV") { "development" } 23 | 24 | # Specifies the `pidfile` that Puma will use. 25 | pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } 26 | 27 | # Specifies the number of `workers` to boot in clustered mode. 28 | # Workers are forked web server processes. If using threads and workers together 29 | # the concurrency of the application would be max `threads` * `workers`. 30 | # Workers do not work on JRuby or Windows (both of which do not support 31 | # processes). 32 | # 33 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 34 | 35 | # Use the `preload_app!` method when specifying a `workers` number. 36 | # This directive tells Puma to first boot the application and load code 37 | # before forking the application. This takes advantage of Copy On Write 38 | # process behavior so workers use less memory. 39 | # 40 | # preload_app! 41 | 42 | # Allow puma to be restarted by `rails restart` command. 43 | plugin :tmp_restart 44 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html 3 | post 'wx_pay' => 'pay#wx_pay' 4 | post 'wx_notify' => 'pay#wx_notify' 5 | get 'wx_notify' => 'pay#wx_notify' 6 | root to: 'home#index' 7 | resource :wechat, only: [:show, :create] 8 | devise_for :users, controllers: { 9 | omniauth_callbacks: 'users/omniauth_callbacks', 10 | registrations: 'users/registrations', 11 | sessions: "users/sessions" 12 | } 13 | end 14 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | # Shared secrets are available across all environments. 14 | 15 | # shared: 16 | # api_key: a1B2c3D4e5F6 17 | 18 | # Environmental secrets are only available for that specific environment. 19 | 20 | development: 21 | secret_key_base: 721c986f86525a0a680b9a95ad2b51b89bed55c53d243e1968412168e0267d98618a530ddb6cf9588d8479030fdd25c386c6f5e3c8dc0f11ffaedc87494927e3 22 | 23 | test: 24 | secret_key_base: ef562ad5b2cd53267a578b2caf23ed2846c360b37b2187d7b6fec6a576fd28a365ae30ed94a3ce6fa13cacaabdb3fd8d804a810ebe4c20a05cf06de086f8fe56 25 | 26 | # Do not keep production secrets in the unencrypted secrets file. 27 | # Instead, either read values from the environment. 28 | # Or, use `bin/rails secrets:setup` to configure encrypted secrets 29 | # and move the `production:` environment over there. 30 | 31 | production: 32 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 33 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w[ 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ].each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket 23 | 24 | # Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /config/wechat.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | appid: <%= Figaro.env.wechat_app_id %> 3 | secret: <%= Figaro.env.wechat_secret %> 4 | token: <%= Figaro.env.wechat_token %> 5 | 6 | production: 7 | <<: *default 8 | encrypt_mode: <%= Figaro.env.wechat_encrypt_mode == "true" %> 9 | encoding_aes_key: <%= Figaro.env.wechat_encoding_aes_key %> 10 | trusted_domain_fullname: <%= Figaro.env.wechat_trusted_domain_fullname %> 11 | 12 | development: 13 | <<: *default 14 | encrypt_mode: <%= Figaro.env.wechat_encrypt_mode == "true" %> 15 | encoding_aes_key: <%= Figaro.env.wechat_encoding_aes_key %> 16 | trusted_domain_fullname: <%= Figaro.env.wechat_trusted_domain_fullname %> 17 | 18 | test: 19 | <<: *default 20 | access_token: "/tmp/wechat_starter_test_access_token" 21 | jsapi_ticket: "/tmp/wechat_starter_test_jsapi_ticket" 22 | trusted_domain_fullname: <%= Figaro.env.wechat_trusted_domain_fullname %> 23 | -------------------------------------------------------------------------------- /db/migrate/20160202145635_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table(:users) do |t| 4 | ## Database authenticatable 5 | t.string :email, null: false, default: "" 6 | t.string :encrypted_password, null: false, default: "" 7 | 8 | ## Recoverable 9 | t.string :reset_password_token 10 | t.datetime :reset_password_sent_at 11 | 12 | ## Rememberable 13 | t.datetime :remember_created_at 14 | 15 | ## Trackable 16 | t.integer :sign_in_count, default: 0, null: false 17 | t.datetime :current_sign_in_at 18 | t.datetime :last_sign_in_at 19 | t.string :current_sign_in_ip 20 | t.string :last_sign_in_ip 21 | 22 | ## Confirmable 23 | # t.string :confirmation_token 24 | # t.datetime :confirmed_at 25 | # t.datetime :confirmation_sent_at 26 | # t.string :unconfirmed_email # Only if using reconfirmable 27 | 28 | ## Lockable 29 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 30 | # t.string :unlock_token # Only if unlock strategy is :email or :both 31 | # t.datetime :locked_at 32 | 33 | 34 | t.timestamps null: false 35 | end 36 | 37 | add_index :users, :email, unique: true 38 | add_index :users, :reset_password_token, unique: true 39 | # add_index :users, :confirmation_token, unique: true 40 | # add_index :users, :unlock_token, unique: true 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /db/migrate/20160202145932_add_omniauth_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddOmniauthToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :provider, :string 4 | add_column :users, :uid, :string 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /db/migrate/20160202145952_remove_email_uniq_index_from_users.rb: -------------------------------------------------------------------------------- 1 | class RemoveEmailUniqIndexFromUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | remove_index :users, :email 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160202150536_add_user_detail_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddUserDetailToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :nickname, :string 4 | add_column :users, :sex, :integer 5 | add_column :users, :avatar, :string 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20160223133841_add_nickname_unique_index_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddNicknameUniqueIndexToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_index :users, :nickname, unique: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160224054132_add_birthday_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddBirthdayToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :birthday, :datetime 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20180514231720_create_active_storage_tables.active_storage.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from active_storage (originally 20170806125915) 2 | class CreateActiveStorageTables < ActiveRecord::Migration[5.2] 3 | def change 4 | create_table :active_storage_blobs do |t| 5 | t.string :key, null: false 6 | t.string :filename, null: false 7 | t.string :content_type 8 | t.text :metadata 9 | t.bigint :byte_size, null: false 10 | t.string :checksum, null: false 11 | t.datetime :created_at, null: false 12 | 13 | t.index [ :key ], unique: true 14 | end 15 | 16 | create_table :active_storage_attachments do |t| 17 | t.string :name, null: false 18 | t.references :record, null: false, polymorphic: true, index: false 19 | t.references :blob, null: false 20 | 21 | t.datetime :created_at, null: false 22 | 23 | t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema[6.1].define(version: 2018_05_14_231720) do 14 | 15 | # These are extensions that must be enabled in order to support this database 16 | enable_extension "plpgsql" 17 | 18 | create_table "active_storage_attachments", force: :cascade do |t| 19 | t.string "name", null: false 20 | t.string "record_type", null: false 21 | t.bigint "record_id", null: false 22 | t.bigint "blob_id", null: false 23 | t.datetime "created_at", null: false 24 | t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" 25 | t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true 26 | end 27 | 28 | create_table "active_storage_blobs", force: :cascade do |t| 29 | t.string "key", null: false 30 | t.string "filename", null: false 31 | t.string "content_type" 32 | t.text "metadata" 33 | t.bigint "byte_size", null: false 34 | t.string "checksum", null: false 35 | t.datetime "created_at", null: false 36 | t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true 37 | end 38 | 39 | create_table "users", id: :serial, force: :cascade do |t| 40 | t.string "email", default: "", null: false 41 | t.string "encrypted_password", default: "", null: false 42 | t.string "reset_password_token" 43 | t.datetime "reset_password_sent_at" 44 | t.datetime "remember_created_at" 45 | t.integer "sign_in_count", default: 0, null: false 46 | t.datetime "current_sign_in_at" 47 | t.datetime "last_sign_in_at" 48 | t.string "current_sign_in_ip" 49 | t.string "last_sign_in_ip" 50 | t.datetime "created_at", null: false 51 | t.datetime "updated_at", null: false 52 | t.string "provider" 53 | t.string "uid" 54 | t.string "nickname" 55 | t.integer "sex" 56 | t.string "avatar" 57 | t.datetime "birthday" 58 | t.index ["nickname"], name: "index_users_on_nickname", unique: true 59 | t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/lib/assets/.keep -------------------------------------------------------------------------------- /lib/devise_failure.rb: -------------------------------------------------------------------------------- 1 | class DeviseFailure < Devise::FailureApp 2 | def redirect_url 3 | user_wechat_omniauth_authorize_path 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/lib/tasks/.keep -------------------------------------------------------------------------------- /lib/tasks/wechat.rake: -------------------------------------------------------------------------------- 1 | namespace 'wechat' do 2 | desc 'show wechat menu' 3 | task 'menu' => :environment do 4 | puts Wechat.api.menu 5 | end 6 | 7 | desc 'create wechat menu' 8 | task 'menu_create' => :environment do 9 | menu = YAML.load(File.read("#{Rails.root}/config/menu_#{Rails.env}.yaml")) 10 | puts 'Menu created' if Wechat.api.menu_create(menu) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/log/.keep -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wechat-starter", 3 | "private": true, 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400] 5 | end 6 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/home_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class HomeControllerTest < ActionController::TestCase 4 | test 'should get index' do 5 | user = User.first 6 | sign_in user 7 | get :index 8 | assert_response :success 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/controllers/pay_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PayControllerTest < ActionController::TestCase 4 | test 'should get index' do 5 | user = User.first 6 | sign_in user 7 | post :wx_pay, xhr: true 8 | assert_response :success 9 | assert_equal 'application/json', @response.content_type 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/test/fixtures/.keep -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/test/fixtures/files/.keep -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | # This model initially had no columns defined. If you add columns to the 4 | # model remove the '{}' from the fixture names and add the columns immediately 5 | # below each fixture, per the syntax in the comments below 6 | # 7 | one: 8 | provider: wechat 9 | uid: 12345 10 | nickname: wechat 11 | 12 | # 13 | two: {} 14 | # column: value 15 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/test/helpers/.keep -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/test/integration/.keep -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/test/mailers/.keep -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/test/models/.keep -------------------------------------------------------------------------------- /test/models/user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/system/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/test/system/.keep -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | class ActiveSupport::TestCase 6 | include Devise::Test::ControllerHelpers 7 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 8 | fixtures :all 9 | 10 | # Add more helper methods to be used by all tests here... 11 | end 12 | -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/tmp/.keep -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/vendor/.keep -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Eric-Guo/wechat-starter/d44eba11589600981ed22fa5733d4885d0358385/vendor/assets/stylesheets/.keep -------------------------------------------------------------------------------- /wechat-starter.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "path": ".", 6 | "folder_exclude_patterns": [".bundle",".idea","tmp",".capistrano","log","tmp","public/assets"], 7 | "file_exclude_patterns": ["*.sublime-workspace",".byebug_history"] 8 | } 9 | ], 10 | "settings": 11 | { 12 | "translate_tabs_to_spaces": true, 13 | "trim_trailing_white_space_on_save": true, 14 | "tab_size": 2 15 | } 16 | } 17 | --------------------------------------------------------------------------------