Browse Source

update flask to lastest,modify runserver.py

remove manager.py
refactor exsition load
honmaple 7 years ago
parent
commit
594df73fa1

+ 7 - 7
README.org

@@ -49,26 +49,26 @@
     #+BEGIN_SRC shell
     mv config.example config.py
     #+END_SRC
-    remember to modify config file.
+    *remember to modify config file.*
 
 *** Init sql
     #+BEGIN_SRC python
-    python manager.py db init 
-    python manager.py db migrate -m "first migrate"
-    python manager.py db upgrade
+    python runserver.py db init 
+    python runserver.py db migrate -m "first migrate"
+    python runserver.py db upgrade
     #+END_SRC
     Or
     #+BEGIN_SRC sh
-    python manager.py init_db
+    python runserver.py initdb
     #+END_SRC
 
 *** create full text index
     #+BEGIN_SRC sh
-    python manager.py create_index
+    python runserver.py create_index
     #+END_SRC
 *** Create admin account
     #+BEGIN_SRC shell
-    python manager.py create_user
+    python runserver.py create_user
     #+END_SRC
 
 *** Login and visit admin 

+ 7 - 7
README_zh.org

@@ -45,27 +45,27 @@
     #+BEGIN_SRC shell
     mv config.example config.py
     #+END_SRC
-    记得修改配置文件
+    *记得修改配置文件*
 
 *** 初始化数据库
     #+BEGIN_SRC python
-    python manager.py db init
-    python manager.py db migrate -m "first migrate"
-    python manager.py db upgrade
+    python runserver.py db init
+    python runserver.py db migrate -m "first migrate"
+    python runserver.py db upgrade
     #+END_SRC
     或者
     #+BEGIN_SRC sh
-    python manager.py init_db
+    python runserver.py initdb
     #+END_SRC
 
 *** 创建全文搜索索引
     #+BEGIN_SRC sh
-    python manager.py create_index
+    python runserver.py create_index
     #+END_SRC
 
 *** 创建管理员账户
     #+BEGIN_SRC shell
-    python manager.py create_user
+    python runserver.py create_user
     #+END_SRC
 
 *** 登陆并访问后台

+ 0 - 4
babel.cfg

@@ -1,4 +0,0 @@
-[ignore: **/static/**.html]
-[python: **.py]
-[jinja2: **.html]
-extensions=jinja2.ext.autoescape,jinja2.ext.with_

+ 22 - 8
config.example

@@ -2,11 +2,11 @@
 # -*- coding=UTF-8 -*-
 # **************************************************************************
 # Copyright © 2016 jianglin
-# File Name: config.py
+# File Name: config.example
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-05-20 12:31:46 (CST)
-# Last Update:星期五 2018-01-05 00:48:08 (CST)
+# Last Update: 星期日 2018-02-11 15:43:26 (CST)
 #          By: jianglin
 # Description:
 # **************************************************************************
@@ -61,15 +61,29 @@ MAIL_PASSWORD = "your password"
 MAIL_DEFAULT_SENDER = 'your email'
 # MAIL_SUPPRESS_SEND = True
 
-# Log,if SEND_LOGS is True when web app has some error happen(500)
-# the email will be sent to RECEIVER
-SEND_LOGS = True
-RECEIVER = ["yourname@gmail.com"]
-INFO_LOG = "info.log"
-ERROR_LOG = "error.log"
 SERVER_NAME = 'localhost:8000'
 SUBDOMAIN = {'forums': True, 'docs': True}
 
+# logging setting
+LOGGING = {
+    'info': 'logs/info.log',
+    'error': 'logs/error.log',
+    'send_mail': False,
+    'toaddrs': [],
+    'subject': 'Your Application Failed',
+    'formatter': '''
+            Message type:       %(levelname)s
+            Location:           %(pathname)s:%(lineno)d
+            Module:             %(module)s
+            Function:           %(funcName)s
+            Time:               %(asctime)s
+
+            Message:
+
+            %(message)s
+            '''
+}
+
 # Sql
 SQLALCHEMY_DATABASE_URI = 'postgresql://postgres:password@localhost/your_db'
 # SQLALCHEMY_DATABASE_URI = 'sqlite:///test.db'

+ 3 - 4
forums/__init__.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-01-25 20:10:50 (CST)
-# Last Update:星期五 2018-01-05 00:19:34 (CST)
+# Last Update: 星期日 2018-02-11 15:05:26 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -15,7 +15,7 @@ import os
 from flask import Flask
 
 from forums import app as ap, extension
-from forums import filters, logs, subdomain
+from forums import jinja, subdomain
 from forums import api, docs, admin
 from flask_maple import auth
 
@@ -31,8 +31,7 @@ def create_app(config):
 
     subdomain.init_app(app)
     ap.init_app(app)
-    filters.init_app(app)
-    logs.init_app(app)
+    jinja.init_app(app)
     extension.init_app(app)
     admin.init_app(app)
     # router

+ 2 - 2
forums/api/message/models.py

@@ -6,14 +6,14 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-04-01 18:33:37 (CST)
-# Last Update:星期三 2017-12-13 16:06:36 (CST)
+# Last Update: 星期日 2018-02-11 15:06:58 (CST)
 #          By:
 # Description:
 # **************************************************************************
 from flask import url_for
 from flask_login import current_user
 from forums.extension import db
-from forums.filters import safe_markdown
+from forums.jinja import safe_markdown
 from forums.common.models import CommonTimeMixin
 
 

+ 2 - 2
forums/api/topic/models.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 20:52:07 (CST)
-# Last Update:星期三 2017-12-13 16:06:36 (CST)
+# Last Update: 星期日 2018-02-11 15:06:01 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -21,7 +21,7 @@ from forums.api.user.models import User
 from forums.common.models import CommonUserMixin
 from forums.extension import db
 from forums.count import Count
-from forums.filters import safe_markdown, safe_clean, markdown
+from forums.jinja import safe_markdown, safe_clean, markdown
 
 topic_follower = db.Table(
     'topic_follower',

+ 2 - 2
forums/api/topic/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 22:07:39 (CST)
-# Last Update:星期三 2017-5-10 16:35:37 (CST)
+# Last Update: 星期日 2018-02-11 15:07:05 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -26,7 +26,7 @@ from forums.common.serializer import Serializer
 from forums.common.utils import gen_filter_dict, gen_order_by
 from forums.common.views import BaseMethodView as MethodView
 from forums.common.views import IsAuthMethodView, IsConfirmedMethodView
-from forums.filters import safe_markdown
+from forums.jinja import safe_markdown
 
 from .models import Reply, Topic
 from .permissions import (like_permission, reply_list_permission,

+ 0 - 125
forums/extension.py

@@ -1,125 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# **************************************************************************
-# Copyright © 2016 jianglin
-# File Name: extension.py
-# Author: jianglin
-# Email: xiyang0807@gmail.com
-# Created: 2016-10-25 21:57:10 (CST)
-# Last Update:星期日 2018-01-07 22:18:41 (CST)
-#          By:
-# Description:
-# **************************************************************************
-from flask import request, g, current_app
-from flask_wtf.csrf import CSRFProtect
-from flask_babelex import Babel, Domain
-from flask_babelex import lazy_gettext as _
-from flask_avatar import Avatar
-from flask_maple.middleware import Middleware
-from flask_maple.models import db
-from flask_maple.app import App
-from flask_maple.json import CustomJSONEncoder
-from flask_maple.bootstrap import Bootstrap
-from flask_maple.error import Error
-from flask_maple.captcha import Captcha
-from flask_maple.redis import Redis
-from flask_maple.mail import Mail
-from flask_principal import Principal
-from flask_login import LoginManager
-from flask_msearch import Search
-from flask_caching import Cache
-import os
-
-
-def register_babel():
-    base_path = os.path.abspath(os.path.dirname(__file__))
-    translations = os.path.join(base_path, os.pardir, 'translations')
-    domain = Domain(translations)
-    babel = Babel(default_domain=domain)
-
-    @babel.localeselector
-    def get_locale():
-        user = getattr(g, 'user', None)
-        if user is not None:
-            if request.path.startswith('/admin'):
-                return 'zh_Hans_CN'
-            if g.user.is_authenticated:
-                return user.setting.locale or 'zh'
-        return request.accept_languages.best_match(current_app.config[
-            'LANGUAGES'].keys())
-
-    @babel.timezoneselector
-    def get_timezone():
-        user = getattr(g, 'user', None)
-        if user is not None:
-            if g.user.is_authenticated:
-                return user.setting.timezone or 'UTC'
-        return 'UTC'
-
-    return babel
-
-
-def register_login():
-    login_manager = LoginManager()
-    login_manager.login_view = "auth.login"
-    login_manager.session_protection = "strong"
-    login_manager.login_message = _("Please login to access this page.")
-    # login_manager.anonymous_user = Anonymous
-
-    @login_manager.user_loader
-    def user_loader(id):
-        from forums.api.user.models import User
-        user = User.query.get(int(id))
-        return user
-
-    # @login_manager.token_loader
-    # def load_token(token):
-    #     return None
-
-    return login_manager
-
-
-babel = register_babel()
-db = db
-csrf = CSRFProtect()
-bootstrap = Bootstrap(
-    css=('styles/monokai.css', 'styles/mine.css'),
-    js=('styles/upload.js', 'styles/forums.js', 'styles/following.js',
-        'styles/topic.js'),
-    use_auth=True)
-captcha = Captcha()
-error = Error()
-redis_data = Redis()
-cache = Cache()
-mail = Mail()
-principal = Principal()
-login_manager = register_login()
-maple_app = App(json=CustomJSONEncoder)
-middleware = Middleware()
-search = Search(db=db)
-
-
-class AvatarCache(Avatar):
-    @cache.cached(
-        timeout=180, key_prefix=lambda: "avatar:{}".format(request.url))
-    def avatar(self, text, width=128):
-        from flask import abort, make_response
-        from flask_avatar.avatar import GenAvatar
-        width_range = current_app.config.get('AVATAR_RANGE', [0, 512])
-        if width < width_range[0] or width > width_range[1]:
-            abort(404)
-        stream = GenAvatar.generate(width, text)
-        buf_value = stream.getvalue()
-        response = make_response(buf_value)
-        response.headers['Content-Type'] = 'image/jpeg'
-        return response
-
-
-avatar = AvatarCache()
-
-
-def init_app(app):
-    for e in [db, avatar, cache, csrf, bootstrap, captcha, error, redis_data,
-              principal, babel, login_manager, maple_app, mail, middleware,
-              search]:
-        e.init_app(app)

+ 64 - 0
forums/extension/__init__.py

@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# ********************************************************************************
+# Copyright © 2018 jianglin
+# File Name: __init__.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2018-02-11 14:52:12 (CST)
+# Last Update: 星期日 2018-02-11 15:31:19 (CST)
+#          By:
+# Description:
+# ********************************************************************************
+from flask import request, current_app
+from flask_wtf.csrf import CSRFProtect
+from flask_avatar import Avatar
+from flask_maple.models import db
+from flask_maple.redis import Redis
+from flask_maple.mail import Mail
+from flask_principal import Principal
+from flask_msearch import Search
+from flask_caching import Cache
+from . import babel, login, maple
+
+db = db
+csrf = CSRFProtect()
+redis_data = Redis()
+cache = Cache()
+mail = Mail()
+principal = Principal()
+search = Search(db=db)
+
+
+class AvatarCache(Avatar):
+    @cache.cached(
+        timeout=180, key_prefix=lambda: "avatar:{}".format(request.url))
+    def avatar(self, text, width=128):
+        from flask import abort, make_response
+        from flask_avatar.avatar import GenAvatar
+        width_range = current_app.config.get('AVATAR_RANGE', [0, 512])
+        if width < width_range[0] or width > width_range[1]:
+            abort(404)
+        stream = GenAvatar.generate(width, text)
+        buf_value = stream.getvalue()
+        response = make_response(buf_value)
+        response.headers['Content-Type'] = 'image/jpeg'
+        return response
+
+
+avatar = AvatarCache()
+
+
+def init_app(app):
+    db.init_app(app)
+    avatar.init_app(app)
+    cache.init_app(app)
+    csrf.init_app(app)
+    principal.init_app(app)
+    redis_data.init_app(app)
+    mail.init_app(app)
+    search.init_app(app)
+
+    babel.init_app(app)
+    login.init_app(app)
+    maple.init_app(app)

+ 46 - 0
forums/extension/babel.py

@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# ********************************************************************************
+# Copyright © 2018 jianglin
+# File Name: babel.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2018-02-11 14:52:25 (CST)
+# Last Update: 星期日 2018-02-11 15:31:25 (CST)
+#          By:
+# Description:
+# ********************************************************************************
+from flask import request, g, current_app
+from flask_babelex import Babel, Domain
+import os
+
+translations = os.path.abspath(
+    os.path.join(
+        os.path.dirname(__file__), os.pardir, os.pardir, 'translations'))
+domain = Domain(translations)
+babel = Babel(default_domain=domain)
+
+
+@babel.localeselector
+def locale():
+    user = getattr(g, 'user', None)
+    if user is not None:
+        if request.path.startswith('/admin'):
+            return 'zh_Hans_CN'
+        if g.user.is_authenticated:
+            return user.setting.locale or 'zh'
+    return request.accept_languages.best_match(current_app.config['LANGUAGES']
+                                               .keys())
+
+
+@babel.timezoneselector
+def timezone():
+    user = getattr(g, 'user', None)
+    if user is not None:
+        if g.user.is_authenticated:
+            return user.setting.timezone or 'UTC'
+    return 'UTC'
+
+
+def init_app(app):
+    babel.init_app(app)

+ 31 - 0
forums/extension/login.py

@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# ********************************************************************************
+# Copyright © 2018 jianglin
+# File Name: login.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2018-02-11 14:54:38 (CST)
+# Last Update: 星期日 2018-02-11 15:26:53 (CST)
+#          By:
+# Description:
+# ********************************************************************************
+from flask_login import LoginManager
+from flask_babelex import lazy_gettext as _
+
+login_manager = LoginManager()
+
+
+@login_manager.user_loader
+def user_loader(id):
+    from forums.api.user.models import User
+    user = User.query.get(int(id))
+    return user
+
+
+def init_app(app):
+    login_manager.login_view = "auth.login"
+    login_manager.session_protection = "strong"
+    login_manager.login_message = _("Please login to access this page.")
+    # login_manager.anonymous_user = Anonymous
+    login_manager.init_app(app)

+ 34 - 0
forums/extension/maple.py

@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# ********************************************************************************
+# Copyright © 2018 jianglin
+# File Name: maple.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2018-02-11 14:56:08 (CST)
+# Last Update: 星期日 2018-02-11 15:26:53 (CST)
+#          By:
+# Description:
+# ********************************************************************************
+from flask_maple.bootstrap import Bootstrap
+from flask_maple.captcha import Captcha
+from flask_maple.error import Error
+from flask_maple.app import App
+from flask_maple.json import CustomJSONEncoder
+from flask_maple.middleware import Middleware
+from flask_maple.log import Logging
+
+bootstrap = Bootstrap(
+    css=('styles/monokai.css', 'styles/mine.css'),
+    js=('styles/upload.js', 'styles/forums.js', 'styles/following.js',
+        'styles/topic.js'),
+    use_auth=True)
+
+
+def init_app(app):
+    bootstrap.init_app(app)
+    Captcha(app)
+    Error(app)
+    App(app, json=CustomJSONEncoder)
+    Middleware(app)
+    Logging(app)

+ 2 - 3
forums/filters.py → forums/jinja.py

@@ -2,11 +2,11 @@
 # -*- coding: utf-8 -*-
 # **************************************************************************
 # Copyright © 2016 jianglin
-# File Name: filters.py
+# File Name: jinja.py
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-11-07 21:00:32 (CST)
-# Last Update:星期二 2017-9-19 12:50:24 (CST)
+# Last Update: 星期日 2018-02-11 15:06:58 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -16,7 +16,6 @@ from config import SITE
 from bleach import clean
 from flask import Markup, g
 from flask_babelex import format_datetime
-from flask_login import current_user
 from misaka import HtmlRenderer, Markdown
 
 

+ 0 - 69
forums/logs.py

@@ -1,69 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# **************************************************************************
-# Copyright © 2016 jianglin
-# File Name: urls.py
-# Author: jianglin
-# Email: xiyang0807@gmail.com
-# Created: 2016-11-07 21:43:17 (CST)
-# Last Update:星期二 2017-9-19 12:55:28 (CST)
-#          By:
-# Description:
-# **************************************************************************
-import os
-import logging
-from logging.handlers import SMTPHandler
-from logging import Formatter
-
-
-def init_app(app):
-    config = app.config
-    logs_folder = os.path.abspath(
-        os.path.join(os.path.dirname(__file__), os.pardir, 'logs'))
-    if not os.path.exists(logs_folder):
-        os.mkdir(logs_folder)
-    formatter = Formatter('''
-        Message type:       %(levelname)s
-        Location:           %(pathname)s:%(lineno)d
-        Module:             %(module)s
-        Function:           %(funcName)s
-        Time:               %(asctime)s
-
-        Message:
-
-        %(message)s
-        ''')
-
-    info_log = os.path.join(logs_folder, config['INFO_LOG'])
-
-    info_file_handler = logging.handlers.RotatingFileHandler(
-        info_log, maxBytes=100000, backupCount=10)
-
-    info_file_handler.setLevel(logging.INFO)
-    info_file_handler.setFormatter(formatter)
-    app.logger.addHandler(info_file_handler)
-
-    error_log = os.path.join(logs_folder, config['ERROR_LOG'])
-
-    error_file_handler = logging.handlers.RotatingFileHandler(
-        error_log, maxBytes=100000, backupCount=10)
-
-    error_file_handler.setLevel(logging.ERROR)
-    error_file_handler.setFormatter(formatter)
-    app.logger.addHandler(error_file_handler)
-
-    if app.config["SEND_LOGS"]:
-        from .common.helper import ThreadedSMTPHandler
-        credentials = (config['MAIL_USERNAME'], config['MAIL_PASSWORD'])
-        mailhost = (config['MAIL_SERVER'], config['MAIL_PORT'])
-        mail_handler = ThreadedSMTPHandler(
-            secure=(),
-            mailhost=mailhost,
-            fromaddr=config['MAIL_DEFAULT_SENDER'],
-            toaddrs=config['RECEIVER'],
-            subject='Your Application Failed',
-            credentials=credentials)
-
-        mail_handler.setLevel(logging.ERROR)
-        mail_handler.setFormatter(formatter)
-        app.logger.addHandler(mail_handler)

+ 0 - 186
manager.py

@@ -1,186 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-# **************************************************************************
-# Copyright © 2016 jianglin
-# File Name: manager.py
-# Author: jianglin
-# Email: xiyang0807@gmail.com
-# Created: 2016-10-25 22:08:39 (CST)
-# Last Update:星期五 2018-01-05 00:35:45 (CST)
-#          By:
-# Description:
-# **************************************************************************
-from flask_script import Manager
-from flask_migrate import Migrate, MigrateCommand
-from forums import create_app
-from forums.extension import db, cache
-from forums.api.user.models import User, UserInfo, UserSetting
-from getpass import getpass
-from datetime import datetime
-import os
-
-app = create_app('config')
-migrate = Migrate(app, db)
-manager = Manager(app)
-
-
-@manager.command
-def create_index():
-    from forums.extension import search
-    return search.create_index()
-
-
-@manager.command
-def update_index():
-    from forums.extension import search
-    return search.create_index(update=True)
-
-
-@manager.command
-def delete_index():
-    from forums.extension import search
-    return search.create_index(delete=True)
-
-
-@manager.command
-def clear_cache():
-    with app.app_context():
-        cache.clear()
-
-
-@manager.command
-def test_index():
-    from forums.extension import search
-    from forums.api.topic.models import Topic
-    results = search.whoosh_search(Topic, '河海', ['title'], 1)
-    print('results:')
-    print(results)
-    for i in results:
-        print(i['title'])
-        print(i.highlights("title"))  # 高亮标题中的检索词
-
-
-@manager.command
-def runserver():
-    return app.run()
-
-
-@manager.command
-def init_perm():
-    from forums.api.user.models import Group
-    anonymous = Group.query.filter_by(name='anonymous').first()
-    if not anonymous:
-        anonymous = Group(name='anonymous')
-        anonymous.save()
-    logined = Group.query.filter_by(name='logined').first()
-    if not logined:
-        logined = Group(name='logined')
-        logined.save()
-    for rule in app.url_map.iter_rules():
-        # print(rule.rule, rule.subdomain, rule.methods, rule.endpoint)
-        print(rule.endpoint)
-        methods = []
-        for method in rule.methods:
-            methods.append(method)
-            method = 'get' if method in ['HEAD', 'OPTIONS'] else method.lower()
-            if not rule.endpoint.startswith('admin'):
-                anonymous.add_perm(
-                    method,
-                    rule.endpoint,
-                    description='anonymous组允许{}'.format(methods))
-                logined.add_perm(
-                    method,
-                    rule.endpoint,
-                    description='logined组允许{}'.format(methods))
-
-
-@manager.command
-def init_db():
-    """
-    Drops and re-creates the SQL schema
-    """
-    db.drop_all()
-    db.configure_mappers()
-    db.create_all()
-    db.session.commit()
-
-
-@manager.command
-def babel_init():
-    pybabel = 'pybabel'
-    os.system(pybabel +
-              ' extract -F babel.cfg -k lazy_gettext -o messages.pot forums')
-    os.system(pybabel + ' init -i messages.pot -d translations -l zh')
-    os.unlink('messages.pot')
-
-
-@manager.command
-def babel_update():
-    pybabel = 'pybabel'
-    os.system(
-        pybabel +
-        ' extract -F babel.cfg -k lazy_gettext -o messages.pot forums templates'
-    )
-    os.system(pybabel + ' update -i messages.pot -d translations')
-    os.unlink('messages.pot')
-
-
-@manager.command
-def babel_compile():
-    pybabel = 'pybabel'
-    os.system(pybabel + ' compile -d translations')
-
-
-@manager.option('-u', '--username', dest='username')
-def delete_user(username):
-    user = User.query.filter_by(username=username).first()
-    user.delete()
-
-
-@manager.option('-u', '--username', dest='username')
-def password_user(username):
-    password = getpass('Password:')
-    user = User.query.filter_by(username=username).first()
-    user.set_password(password)
-    user.save()
-
-
-@manager.option('-u', '--username', dest='username')
-@manager.option('-e', '--email', dest='email')
-@manager.option('-w', '--password', dest='password')
-def create_user(username, email, password):
-    if username is None:
-        username = input('Username(default admin):') or 'admin'
-    if email is None:
-        email = input('Email:')
-    if password is None:
-        password = getpass('Password:')
-    user = User(username=username, email=email)
-    user.set_password(password)
-    user.is_superuser = True
-    user.is_confirmed = True
-    user.save()
-
-
-@manager.option('-h', '--host', dest='host', default='127.0.0.1')
-@manager.option('-p', '--port', dest='port', type=int, default=8000)
-@manager.option('-w', '--workers', dest='workers', type=int, default=2)
-def gunicorn(host, port, workers):
-    """use gunicorn"""
-    from gunicorn.app.base import Application
-
-    class FlaskApplication(Application):
-        def init(self, parser, opts, args):
-            return {'bind': '{0}:{1}'.format(host, port), 'workers': workers}
-
-        def load(self):
-            return app
-
-    application = FlaskApplication()
-    return application.run()
-
-
-manager.add_command('db', MigrateCommand)
-
-if __name__ == '__main__':
-    manager.run()

+ 10 - 9
requirements.txt

@@ -1,29 +1,30 @@
-bleach==1.4.3
+bleach==2.1.2
+cffi==1.11.4
 cssmin==0.2.0
-Flask==0.11.1
+Flask==0.12.2
 Flask-Admin==1.4.2
 Flask-Assets==0.12
 Flask-Avatar==0.1.0
 Flask-BabelEx==0.9.3
 Flask-Caching==1.3.2
-Flask-Login==0.3.2
+Flask-Login==0.4.1
 Flask-Mail==0.9.1
+Flask-Maple==0.5.2
 Flask-Migrate==1.8.0
+flask-msearch==0.1.5
 Flask-Principal==0.4.0
-Flask-Script==2.0.5
 Flask-SQLAlchemy==2.2
 Flask-WTF==0.14.2
-flask-msearch==0.1.5
-flask-maple==0.4.7
-Whoosh==2.7.4
-gevent==1.1.1
 gunicorn==19.4.5
 hiredis==0.2.0
-Jinja2==2.8
+Jinja2==2.10
 jsmin==2.2.0
 misaka==2.0.0
 Pillow==3.2.0
 psycopg2==2.6.1
 Pygments==2.1
+pytz==2018.3
 redis==2.10.5
 SQLAlchemy==1.1.6
+Whoosh==2.7.4
+WTForms==2.1

+ 158 - 3
runserver.py

@@ -6,15 +6,170 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-10-25 22:01:29 (CST)
-# Last Update:星期五 2017-11-10 11:14:52 (CST)
+# Last Update: 星期日 2018-02-11 15:24:50 (CST)
 #          By:
 # Description:
 # **************************************************************************
-from forums import create_app
+from flask import current_app
+from flask.cli import FlaskGroup, run_command
 from werkzeug.contrib.fixers import ProxyFix
+from code import interact
+from getpass import getpass
+
+from forums.extension import db, cache, search
+from forums import create_app
+from forums.api.user.models import User
+
+import click
+import os
+import sys
 
 app = create_app('config')
 app.wsgi_app = ProxyFix(app.wsgi_app)
 
+cli = FlaskGroup(add_default_commands=False, create_app=lambda r: app)
+cli.add_command(run_command)
+
+try:
+    from flask_migrate import Migrate
+    migrate = Migrate(app, db)
+except ImportError:
+    pass
+
+
+@cli.command('shell', short_help='Starts an interactive shell.')
+def shell_command():
+    ctx = current_app.make_shell_context()
+    interact(local=ctx)
+
+
+@cli.command()
+def runserver():
+    app.run()
+
+
+@cli.command()
+def create_index():
+    return search.create_index()
+
+
+@cli.command()
+def update_index():
+    return search.create_index(update=True)
+
+
+@cli.command()
+def delete_index():
+    return search.create_index(delete=True)
+
+
+@cli.command()
+def clear_cache():
+    cache.clear()
+
+
+@cli.command()
+def init_perm():
+    from forums.api.user.models import Group
+    anonymous = Group.query.filter_by(name='anonymous').first()
+    if not anonymous:
+        anonymous = Group(name='anonymous')
+        anonymous.save()
+    logined = Group.query.filter_by(name='logined').first()
+    if not logined:
+        logined = Group(name='logined')
+        logined.save()
+    for rule in app.url_map.iter_rules():
+        # print(rule.rule, rule.subdomain, rule.methods, rule.endpoint)
+        print(rule.endpoint)
+        methods = []
+        for method in rule.methods:
+            methods.append(method)
+            method = 'get' if method in ['HEAD', 'OPTIONS'] else method.lower()
+            if not rule.endpoint.startswith('admin'):
+                anonymous.add_perm(
+                    method,
+                    rule.endpoint,
+                    description='anonymous组允许{}'.format(methods))
+                logined.add_perm(
+                    method,
+                    rule.endpoint,
+                    description='logined组允许{}'.format(methods))
+
+
+@cli.command()
+def initdb():
+    """
+    Drops and re-creates the SQL schema
+    """
+    db.drop_all()
+    db.configure_mappers()
+    db.create_all()
+    db.session.commit()
+
+
+@cli.command()
+@click.option('-l', '--lang', default='zh')
+def babel_init(lang):
+    babel_conf = "translations/babel.cfg"
+    src_path = ["forums", "templates"]
+    os.system('pybabel extract -F {0} -k lazy_gettext -o messages.pot {1}'.
+              format(babel_conf, ' '.join(src_path)))
+    os.system('pybabel init -i messages.pot -d translations -l {0}'.format(
+        lang))
+    os.unlink('messages.pot')
+
+
+@cli.command()
+def babel_update():
+    babel_conf = "translations/babel.cfg"
+    src_path = ["forums", "templates"]
+    os.system('pybabel extract -F {0} -k lazy_gettext -o messages.pot {1}'.
+              format(babel_conf, ' '.join(src_path)))
+    os.system('pybabel update -i messages.pot -d translations')
+    os.unlink('messages.pot')
+
+
+@cli.command()
+def babel_compile():
+    os.system('pybabel compile -d translations')
+
+
+@cli.command()
+@click.option('-u', '--username')
+def delete_user(username):
+    user = User.query.filter_by(username=username).first()
+    user.delete()
+
+
+@cli.command()
+@click.option('-u', '--username')
+def password_user(username):
+    password = getpass('Password:')
+    user = User.query.filter_by(username=username).first()
+    user.set_password(password)
+    user.save()
+
+
+@cli.command()
+@click.option('-u', '--username')
+@click.option('-e', '--email')
+@click.option('-w', '--password')
+def create_user(username, email, password):
+    if username is None:
+        username = input('Username(default admin):') or 'admin'
+    if email is None:
+        email = input('Email:')
+    if password is None:
+        password = getpass('Password:')
+    user = User(
+        username=username, email=email, is_superuser=True, is_confirmed=True)
+    user.set_password(password)
+    user.save()
+
+
 if __name__ == '__main__':
-    app.run(port=8000)
+    if len(sys.argv) == 1:
+        app.run()
+    else:
+        cli.main()

+ 1 - 1
templates/forums/_macro.html

@@ -27,7 +27,7 @@
         <a href="{{ url }}">
             <div class="panel-footer">
                 {{ name }}
-                <i class="pull-right fa-arrow-right"></i>
+                <i class="pull-right fa fa-arrow-right"></i>
             </div>
         </a>
     </div>

+ 1 - 1
templates/topic/reply/itemlist.html

@@ -16,7 +16,7 @@
   <div class="panel-body media" id="reply-{{ reply.id }}" style="border-bottom:1px solid #eee;margin:0">
     <div class="media-left">
       <a href="{{ url_for('user.user',username=reply.author.username) }}">
-        <img class="media-object img-circle" src="{{ url_for('avatar',text=user.username) }}" alt="avatar" style="width:64px;height:64px"/>
+        <img class="media-object img-circle" src="{{ url_for('avatar',text=user.username) }}" alt="avatar" style="width:48px;height:48px"/>
       </a>
     </div>
     <div class="media-body">

+ 4 - 0
translations/babel.cfg

@@ -0,0 +1,4 @@
+[ignore: ../**/static/**.html]
+[python: ../**.py]
+[jinja2: ../templates/**.html]
+extensions=jinja2.ext.autoescape,jinja2.ext.with_