Skip to content
Browse files

各种修改.. MySQL 性能优化.. 等

  • Loading branch information...
1 parent 241af8a commit 314b03e2c26272a282050272712bcd1ca5aa15bf @fanzeyi committed
Showing with 136 additions and 73 deletions.
  1. +9 −4 backstage.py
  2. +7 −4 forum.py
  3. +2 −1 handlers.py
  4. +4 −2 home.py
  5. +13 −7 judge/base/__init__.py
  6. +6 −4 judge/db/__init__.py
  7. +2 −2 less/style.less
  8. +2 −2 main.py
  9. +42 −8 member.py
  10. +7 −2 problem.py
  11. +2 −2 static/css/style.css
  12. +4 −4 tpl/base/sidebar.html
  13. +9 −4 tpl/home.html
  14. +10 −10 tpl/member.html
  15. +1 −1 tpl/settings.html
  16. +2 −2 tpl/submit.html
  17. +10 −10 tpl/submit_list.html
  18. +2 −2 tpl/topic.html
  19. +1 −1 tpl/topic_list.html
  20. +1 −1 tpl/widget/notice.html
View
13 backstage.py
@@ -2,11 +2,12 @@
# AUTHOR: Zeray Rice <fanzeyi1994@gmail.com>
# FILE: backstage.py
# CREATED: 02:43:49 15/03/2012
-# MODIFIED: 03:31:00 18/04/2012
+# MODIFIED: 18:31:05 18/04/2012
import re
import datetime
import functools
+from sqlalchemy.orm.exc import NoResultFound
from tornado.web import HTTPError
@@ -174,10 +175,14 @@ def post(self):
error.extend(self.check_text_value(name, self._("Name"), required = True, max = 100))
error.extend(self.check_text_value(link, self._("Link"), required = True, max = 100))
if not error:
- duplinode = self.select_node_by_link(link)
- if duplinode:
+ try:
+ duplinode = self.select_node_by_link(link)
+ except NoResultFound:
+ pass
+ else:
error.append(self._("This link have taken."))
- node.id = nid
+ if nid:
+ node.id = nid
node.name = name
node.link = link
node.description = ""
View
11 forum.py
@@ -2,11 +2,12 @@
# AUTHOR: Zeray Rice <fanzeyi1994@gmail.com>
# FILE: forum.py
# CREATED: 22:39:44 17/04/2012
-# MODIFIED: 18:21:14 18/04/2012
+# MODIFIED: 18:52:37 18/04/2012
import datetime
from tornado.web import HTTPError
from tornado.web import authenticated
+from sqlalchemy.orm.exc import NoResultFound
from judge.db import Node
from judge.db import Reply
@@ -57,6 +58,7 @@ def get(self, link):
node = self.select_node_by_link(link.lower())
if not node:
raise HTTPError(404)
+ topic = None
if self.current_user.admin:
tid = self.get_argument("tid", default = 0)
try:
@@ -73,8 +75,9 @@ def get(self, link):
self.render("topic_create.html", locals())
@authenticated
def post(self, link):
- node = self.select_node_by_link(link.lower())
- if not node:
+ try:
+ node = self.select_node_by_link(link.lower())
+ except NoResultFound:
raise HTTPError(404)
title = self.get_argument("title", default = "")
content = self.get_argument("content", default = "")
@@ -157,7 +160,7 @@ def post(self, topic_id):
self.render("topic.html", locals())
return
reply = Reply()
- reply.content = content
+ reply.content = self.xhtml_escape(content)
reply.topic_id = topic.id
reply.member_id = self.current_user.id
reply.create = datetime.datetime.now()
View
3 handlers.py
@@ -2,7 +2,7 @@
# AUTHOR: Zeray Rice <fanzeyi1994@gmail.com>
# FILE: handlers.py
# CREATED: 01:41:06 08/03/2012
-# MODIFIED: 18:18:55 18/04/2012
+# MODIFIED: 20:28:26 18/04/2012
# DESCRIPTION: URL Route
from api import *
@@ -44,5 +44,6 @@
(r'/t/([\d]*)', ViewTopicHandler),
(r'/new/(.*)', CreateTopicHandler),
(r'/forum', ViewForumHandler),
+ (r'/test', TestHandler),
(r'/api/problem/get/([\d]*)', GetProblemHandler),
]
View
6 home.py
@@ -2,17 +2,18 @@
# AUTHOR: Zeray Rice <fanzeyi1994@gmail.com>
# FILE: home.py
# CREATED: 02:00:16 08/03/2012
-# MODIFIED: 19:27:54 17/04/2012
+# MODIFIED: 18:26:17 18/04/2012
# DESCRIPTION: Home handler
from contest import get_contest_status
+from judge.db import ForumDBMixin
from judge.db import MemberDBMixin
from judge.db import ContestDBMixin
from judge.db import ProblemDBMixin
from judge.base import BaseHandler
-class HomeHandler(BaseHandler, MemberDBMixin, ProblemDBMixin, ContestDBMixin):
+class HomeHandler(BaseHandler, MemberDBMixin, ProblemDBMixin, ContestDBMixin, ForumDBMixin):
def get(self):
title = self._("Home")
breadcrumb = []
@@ -23,6 +24,7 @@ def get(self):
for contest in latest_contest:
contest.status = get_contest_status(contest)
latest_topic = []
+ latest_node = self.select_latest_node()
count_problem = self.count_visible_problem()
count_member = self.count_member()
self.render("home.html", locals())
View
20 judge/base/__init__.py
@@ -2,7 +2,7 @@
# AUTHOR: Zeray Rice <fanzeyi1994@gmail.com>
# FILE: judge/base/__init__.py
# CREATED: 01:49:33 08/03/2012
-# MODIFIED: 02:16:21 18/04/2012
+# MODIFIED: 22:24:01 18/04/2012
# DESCRIPTION: Base handler
import re
@@ -20,6 +20,7 @@
from pygments.lexers import CppLexer
from pygments.lexers import DelphiLexer
from pygments.formatters import HtmlFormatter
+from sqlalchemy.orm.exc import NoResultFound
import tornado.web
import tornado.escape
@@ -104,6 +105,7 @@ def render(self, tplname, args = {}):
tpl = self.jinja2.get_template(tplname)
ren = tpl.render(page = self, _ = self._, user = self.current_user, **args)
self.write(ren)
+ self.db.close()
self.finish()
def write_error(self, status_code, **kwargs):
'''Rewrite write_error for custom error page'''
@@ -154,8 +156,11 @@ def check_username(self, usr, queryDB = False):
regex = re.compile(r'^([\w\d]*)$'), \
regex_msg = self._("A username can only contain letters and digits.")))
if not error and queryDB:
- query = self.select_member_by_username_lower(usr.lower())
- if query:
+ try:
+ query = self.select_member_by_username_lower(usr.lower())
+ except NoResultFound:
+ pass
+ else:
error.append(self._("That username is taken. Please choose another."))
return error
def check_password(self, pwd):
@@ -166,8 +171,11 @@ def check_email(self, email, queryDB = False):
regex = re.compile(r"(?:^|\s)[-a-z0-9_.+]+@(?:[-a-z0-9]+\.)+[a-z]{2,6}(?:\s|$)", re.IGNORECASE), \
regex_msg = self._("Your Email address is invalid.")))
if not error and queryDB:
- query = self.select_member_by_email(email)
- if query:
+ try:
+ query = self.select_member_by_email(email)
+ except NoResultFound:
+ pass
+ else:
error.append(self._("That Email is taken. Please choose another."))
return error
def get_gravatar_url(self, email):
@@ -178,8 +186,6 @@ def post_to_judger(self, query, judger, callback = None):
query["code"] = query["code"].decode("utf-8")
query = dict(sorted(query.iteritems(), key=itemgetter(1)))
jsondump = json.dumps(query)
- print jsondump
- print judger.pubkey.strip()
sign = hashlib.sha1(jsondump + judger.pubkey.strip()).hexdigest()
query["sign"] = sign
http_client = AsyncHTTPClient()
View
10 judge/db/__init__.py
@@ -2,7 +2,7 @@
# AUTHOR: Zeray Rice <fanzeyi1994@gmail.com>
# FILE: judge/db/__init__.py
# CREATED: 02:01:23 08/03/2012
-# MODIFIED: 18:18:21 18/04/2012
+# MODIFIED: 20:41:50 18/04/2012
# DESCRIPTION: Database Table Object
import uuid
@@ -26,9 +26,9 @@ class MemberDBMixin(object):
def count_member(self):
return self.db.query(Member).count()
def count_accepted_by_member_id(self, member_id):
- return self.db.query(Submit).filter_by(member_id = 1).filter_by(status = 1).count()
+ return self.db.query(Submit).filter_by(member_id = member_id).filter_by(status = 1).count()
def count_submit_by_member_id(self, member_id):
- return self.db.query(Submit).filter_by(member_id = 1).count()
+ return self.db.query(Submit).filter_by(member_id = member_id).count()
''' SELECT '''
def select_member_by_id(self, member_id):
return self.db.query(Member).get(member_id)
@@ -88,6 +88,8 @@ class ForumDBMixin(object):
def count_reply_by_topic_id(self, topic_id):
return self.db.query(Reply).filter_by(topic_id = topic_id).count()
'''SELECT'''
+ def select_latest_node(self, count = 20):
+ return self.db.query(Node).order_by(desc(Node.id)).limit(count).all()
def select_node_by_id(self, node_id):
return self.db.query(Node).get(node_id)
def select_node_by_link(self, link):
@@ -162,7 +164,7 @@ def select_visible_problem_order_by_id(self, count = 10, start = 0):
def select_latest_visible_problem_order_by_id(self, count = 10):
return self.db.query(Problem).filter_by(invisible = 0).order_by(desc(Problem.id)).limit(count).all()
def select_last_submit_by_problem_id_member_id(self, problem_id):
- return self.db.query(Submit).filter_by(problem_id = problem_id).filter_by(member_id = self.current_user.id).order_by(desc(Submit.id)).all()
+ return self.db.query(Submit).filter_by(problem_id = problem_id).filter_by(member_id = self.current_user.id).order_by(desc(Submit.id)).limit(1).one()
def select_submit_by_id(self, sid):
return self.db.query(Submit).get(sid)
def select_submit_order_by_id(self, count = 10, start = 0):
View
4 less/style.less
@@ -173,8 +173,8 @@ a {
.tag {
.border-radius(4px);
background-color: #EEE;
- padding: 4px 4px 2px 10px;
- float: left;
+ padding: 4px 10px;
+ display: inline;
margin-right: 4px;
margin-bottom: 5px;
View
4 main.py
@@ -2,7 +2,7 @@
# AUTHOR: Zeray Rice <fanzeyi1994@gmail.com>
# FILE: main.py
# CREATED: 01:37:19 08/03/2012
-# MODIFIED: 23:41:07 17/04/2012
+# MODIFIED: 22:08:52 18/04/2012
# DESCRIPTION: Main Server File, run as `python2 main.py [port_num]`
import re
@@ -51,7 +51,7 @@ def __init__(self):
# self.db = tornado.database.Connection(
# host=options.mysql_host, database=options.mysql_database,
# user=options.mysql_user, password=options.mysql_password)
- engine = create_engine(mysql_path, convert_unicode=True, echo=options.debug)
+ engine = create_engine(mysql_path, convert_unicode=True)#, echo=options.debug)
models.init_db(engine)
self.db = scoped_session(sessionmaker(bind=engine))
View
50 member.py
@@ -2,12 +2,16 @@
# AUTHOR: Zeray Rice <fanzeyi1994@gmail.com>
# FILE: member.py
# CREATED: 02:18:23 09/03/2012
-# MODIFIED: 19:24:32 17/04/2012
+# MODIFIED: 20:29:11 18/04/2012
# DESCRIPTION: member handlers
import re
import copy
+import uuid
+import binascii
import bcrypt
+import datetime
+from sqlalchemy.orm.exc import NoResultFound
from tornado.web import HTTPError
from tornado.web import authenticated
@@ -32,8 +36,9 @@ def post(self):
pwd = pwd.encode("utf-8")
pwd = bcrypt.hashpw(pwd, self.settings['bcrypt_salt'])
if not error:
- member = self.select_member_by_usr_pwd(usr, pwd)
- if not member:
+ try:
+ member = self.select_member_by_usr_pwd(usr, pwd)
+ except NoResultFound:
error.append(self._("Wrong Username and password combination."))
if error:
self.render("signin.html", locals())
@@ -42,7 +47,6 @@ def post(self):
self.set_secure_cookie("auth", auth.secret)
self.set_secure_cookie("uid", str(auth.member_id))
go_next = self.get_argument("next", default = None)
- print go_next
if go_next:
self.redirect(go_next)
return
@@ -68,10 +72,12 @@ def post(self):
member = Member()
member.username = usr
member.username_lower = usr.lower()
- member.passowrd = bcrypt.hashpw(pwd, self.settings['bcrypt_salt'])
+ member.password = bcrypt.hashpw(pwd, self.settings['bcrypt_salt'])
member.email = email
member.gravatar_link = self.get_gravatar_url(email)
- self.insert_member(member)
+ member.create = datetime.datetime.now()
+ self.db.add(member)
+ self.db.commit()
auth = self.create_auth(member.id)
self.set_secure_cookie('auth', auth.secret)
self.set_secure_cookie('uid', str(auth.member_id))
@@ -198,7 +204,35 @@ def get(self):
for member in members:
member.accepted = self.count_accepted_by_member_id(member.id)
member.submit = self.count_submit_by_member_id(member.id)
- member.rating = member.accepted / float(member.submit) * 100
+ member.rating = 0.0
+ if member.submit:
+ member.rating = round(float(member.accepted) / member.submit * 100, 2)
self.render("member_list.html", locals())
-__all__ = ["SigninHandler", "SignupHandler", "SignoutHandler", "SettingsHandler", "ChangePasswordHandler", "MemberHandler", "ListMemberHandler"]
+class TestHandler(BaseHandler, MemberDBMixin):
+ def post(self):
+ usr = binascii.b2a_hex(uuid.uuid4().bytes)[3:10]
+ pwd = binascii.b2a_hex(uuid.uuid4().bytes)[3:10]
+ email = usr + "@gmail.com"
+ error = []
+ error.extend(self.check_username(usr.lower(), queryDB = True))
+ error.extend(self.check_password(pwd))
+ error.extend(self.check_email(email, queryDB = True))
+ if error:
+ self.render("signup.html", locals())
+ return
+ member = Member()
+ member.username = usr
+ member.username_lower = usr.lower()
+ member.password = bcrypt.hashpw(pwd, self.settings['bcrypt_salt'])
+ member.email = email
+ member.gravatar_link = self.get_gravatar_url(email)
+ member.create = datetime.datetime.now()
+ self.db.add(member)
+ self.db.commit()
+ auth = self.create_auth(member.id)
+ self.set_secure_cookie('auth', auth.secret)
+ self.set_secure_cookie('uid', str(auth.member_id))
+ self.redirect('/')
+
+__all__ = ["SigninHandler", "SignupHandler", "SignoutHandler", "SettingsHandler", "ChangePasswordHandler", "MemberHandler", "ListMemberHandler", "TestHandler"]
View
9 problem.py
@@ -2,10 +2,11 @@
# AUTHOR: Zeray Rice <fanzeyi1994@gmail.com>
# FILE: problem.py
# CREATED: 04:04:57 15/03/2012
-# MODIFIED: 02:22:02 18/04/2012
+# MODIFIED: 22:08:40 18/04/2012
import os
import time
+from sqlalchemy.orm.exc import NoResultFound
from tornado.web import HTTPError
from tornado.web import asynchronous
@@ -128,7 +129,10 @@ def get(self):
pages = self.get_page_count(count, 20)
if self.current_user:
for problem in problems:
- problem.submit = self.select_last_submit_by_problem_id_member_id(problem.id)
+ try:
+ problem.submit = self.select_last_submit_by_problem_id_member_id(problem.id)
+ except NoResultFound:
+ problem.submit = None
self.render("problem_list.html", locals())
class ViewTagHandler(BaseHandler, ProblemDBMixin):
@@ -171,6 +175,7 @@ def get(self):
submits = self.select_submit_order_by_id(10, start)
pages = self.get_page_count(count)
self.render("submit_list.html", locals())
+# self.db.close()
class ViewSubmitHandler(BaseHandler, ProblemDBMixin):
def get(self, sid):
View
4 static/css/style.css
@@ -3853,8 +3853,8 @@ a.nounderline:hover {
-moz-border-radius: 4px;
border-radius: 4px;
background-color: #EEE;
- padding: 4px 4px 2px 10px;
- float: left;
+ padding: 4px 10px;
+ display: inline;
margin-right: 4px;
margin-bottom: 5px;
}
View
8 tpl/base/sidebar.html
@@ -52,13 +52,13 @@
{{ _('Submit') }}
</a>
</li>
-<!-- <li>
+ <li>
<a href="/forum">
<i class="icon-comment icon-white"></i>
{{ _('Forum') }}
</a>
</li>
- <li>
+<!-- <li>
<a href="/list">
<i class="icon-list icon-white"></i>
{{ _('List') }}
@@ -137,8 +137,8 @@
<div class="copyright">
&copy; 2012 <a href="https://github.com/fanzeyi/Vulpix">Vulpix</a>
| <a href="/lang/en">English</a>
- | <a href="/lang/zh_cn">简体中文</a>
- | <a href="/lang/zh_tw">正體中文</a>
+ | <a href="/lang/zh_cn">简体中文</a> | <br />
+ <a href="/lang/zh_tw">正體中文</a>
| <a href="/help">{{ _('Help') }}</a>
</div>
</nav>
View
13 tpl/home.html
@@ -19,10 +19,10 @@
<thead>
<tr>
<th style="width:20px; text-align:center; ">#</th>
- <th style="width:100px;">{{ _('Status') }}</th>
- <th>{{ _('Problem') }}</th>
- <th style="width:100px">{{ _('User') }}</th>
- <th style="width:50px">{{ _('Language') }}</th>
+ <th>{{ _('Status') }}</th>
+ <th style="width: 100%">{{ _('Problem') }}</th>
+ <th>{{ _('User') }}</th>
+ <th style="white-space:nowrap; ">{{ _('Language') }}</th>
</tr>
</thead>
<tbody>
@@ -63,6 +63,11 @@
<div class="cell">
<div class="title">{{ _('New Node') }}</div>
<div class="body content">
+ <ul class="tagcloud">
+ {% for node in latest_node %}
+ <li class="tag"><a href="/go/{{ node.link }}">{{ node.name }}</a></li>
+ {% endfor %}
+ </ul>
</div>
</div>
<div class="cell" id="count">
View
20 tpl/member.html
@@ -24,12 +24,12 @@
<thead>
<tr>
<th style="width:20px; text-align:center; ">#</th>
- <th style="width:100px;">{{ _('Status') }}</th>
- <th>{{ _('Problem') }}</th>
- <th style="width:50px">{{ _('Language') }}</th>
- <th style="width:50px;">{{ _('Time') }}</th>
- <th style="width:50px;">{{ _('Memory') }}</th>
- <th style="width:100px;">{{ _('Testpoint') }}</th>
+ <th>{{ _('Status') }}</th>
+ <th style="width:100%">{{ _('Problem') }}</th>
+ <th style="white-space: nowrap; ">{{ _('Language') }}</th>
+ <th style="white-space: nowrap; ">{{ _('Time') }}</th>
+ <th style="white-space: nowrap; ">{{ _('Memory') }}</th>
+ <th style="white-space: nowrap; ">{{ _('Testpoint') }}</th>
</tr>
</thead>
<tbody>
@@ -37,11 +37,11 @@
<tr>
<td style="text-align:center">{{ submit.id }}</td>
<td><a href="/submit/{{ submit.id }}" class="nounderline">{{ submit | get_submit_status }}</a></td>
- <td><a href="/problem/{{ submit.problem_id }}">{{ submit.title }}</a></td>
+ <td><a href="/problem/{{ submit.problem_id }}">{{ submit.problem.title }}</a></td>
<td style="text-align:center">{{ submit.lang | lang2humantext }}</td>
- <td>{{ submit.costtime }} ms</td>
- <td>{{ submit.costmemory }} KB</td>
- <td>{% if submit.testpoint %}{{ submit.testpoint }}{% endif %}</td>
+ <td style="white-space: nowrap; ">{{ submit.costtime }} ms</td>
+ <td style="white-space: nowrap; ">{{ submit.costmemory }} KB</td>
+ <td style="white-space: nowrap; ">{% if submit.testpoint %}{{ submit.testpoint }}{% endif %}</td>
</tr>
{% endfor %}
</tbody>
View
2 tpl/settings.html
@@ -8,7 +8,7 @@
{{ show_error_list(error) }}
{% if msg %}
<div class="alert alert-success">
- {{ msg }}
+ {{ msg.decode("utf-8") }}
</div>
{% endif %}
<form method="POST" action="/settings" class="form-horizontal cell form">
View
4 tpl/submit.html
@@ -54,7 +54,7 @@
</table>
</div>
</div>
-{% if submit.status == 6%}
+{% if submit.status == 6 %}
<div class="cell">
<div class="title">{{ _('Compile Output') }}</div>
<div class="body content">
@@ -94,7 +94,7 @@
<div class="cell">
<div class="title">{{ _('Code') }}</div>
<div class="body content">
- {% if submit.member_id == user.id or user.admin or true%}
+ {% if submit.member_id == user.id or user.admin %}
{{ code_highlighted }}
{% else %}
<div class="well" style="margin-bottom: 0; ">{{ _("You dont have permissions to view this code.") }}</div>
View
20 tpl/submit_list.html
@@ -8,13 +8,13 @@
<thead>
<tr>
<th style="width:20px; text-align:center; ">#</th>
- <th style="width:100px;">{{ _('Status') }}</th>
- <th>{{ _('Problem') }}</th>
- <th style="width:100px">{{ _('User') }}</th>
- <th style="width:50px">{{ _('Language') }}</th>
- <th style="width:50px;">{{ _('Time') }}</th>
- <th style="width:50px;">{{ _('Memory') }}</th>
- <th style="width:100px;">{{ _('Testpoint') }}</th>
+ <th>{{ _('Status') }}</th>
+ <th style="width:100%">{{ _('Problem') }}</th>
+ <th>{{ _('User') }}</th>
+ <th style="white-space:nowrap; ">{{ _('Language') }}</th>
+ <th style="white-space:nowrap; ">{{ _('Time') }}</th>
+ <th style="white-space:nowrap; ">{{ _('Memory') }}</th>
+ <th style="white-space:nowrap; ">{{ _('Testpoint') }}</th>
</tr>
</thead>
<tbody>
@@ -25,9 +25,9 @@
<td><a href="/problem/{{ submit.problem_id }}">{{ submit.problem.title }}</a></td>
<td><a href="/member/{{ submit.member.username }}">{{ submit.member.username }}</a></td>
<td style="text-align:center">{{ submit.lang | lang2humantext }}</td>
- <td>{{ submit.costtime }} ms</td>
- <td>{{ submit.costmemory }} KB</td>
- <td>{% if submit.testpoint %}{{ submit.testpoint }}{% endif %}</td>
+ <td style="white-space:nowrap; ">{{ submit.costtime }} ms</td>
+ <td style="white-space:nowrap; ">{{ submit.costmemory }} KB</td>
+ <td style="white-space:nowrap; ">{% if submit.testpoint %}{{ submit.testpoint }}{% endif %}</td>
</tr>
{% endfor %}
</tbody>
View
4 tpl/topic.html
@@ -13,7 +13,7 @@
<div class="body">
<table class="topicContent">
<tr>
- <td class="content">{{ topic.content | autolink }}</td>
+ <td class="content" style="vertical-align:top">{{ topic.content | autolink }}</td>
<td class="author"><a href="/member/{{ topic.member.username }}">{{ topic.member.gravatar_link | avatar_img(70) }}</a></td>
</tr>
</table>
@@ -30,7 +30,7 @@
<table class="replyContent">
<tr>
<td class="avatar" rowspan="2">
- <a href="/member/{{ topic.member.username }}">{{ topic.member.gravatar_link | avatar_img(50) }}</a>
+ <a href="/member/{{ reply.member.username }}">{{ reply.member.gravatar_link | avatar_img(50) }}</a>
</td>
<td class="replyMeta">
#{{ loop.index }} by <a href="/member/{{ reply.member.username }}">{{ reply.member.username}}</a> at {{ reply.create }}
View
2 tpl/topic_list.html
@@ -40,7 +40,7 @@
</div>
{% endif %}
{% if user and node %}
-<form method="POST" action="/new/{#{ node.link }#}" class="form-horizontal cell form">
+<form method="POST" action="/new/{{ node.link }}" class="form-horizontal cell form">
<fieldset>
<div class="title">{{ _('Create Topic') }}</div>
{{ page.xsrf_form_html() }}
View
2 tpl/widget/notice.html
@@ -1 +1 @@
-<p>欢迎来到<a href="">Vulpix</a>. 这里还正在开发中. BUG提交请至<a href="/forum/go/vulpix">讨论区</a>.</p>
+<p>欢迎来到<a href="">Vulpix</a>. 这里还正在开发中. BUG提交请至<a href="/go/vulpix">讨论区</a>.</p>

0 comments on commit 314b03e

Please sign in to comment.
Something went wrong with that request. Please try again.