Add docker support
This commit is contained in:
7
.dockerignore
Normal file
7
.dockerignore
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Игнорировать байткод
|
||||||
|
*.pyc
|
||||||
|
*.pio
|
||||||
|
**/__pycache__/
|
||||||
|
|
||||||
|
# Каталог с данными
|
||||||
|
**/data/
|
||||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,3 +7,6 @@
|
|||||||
**/data/
|
**/data/
|
||||||
/config
|
/config
|
||||||
**/logs/
|
**/logs/
|
||||||
|
|
||||||
|
.env*
|
||||||
|
alembic.ini
|
||||||
|
|||||||
1
alembic/README
Normal file
1
alembic/README
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Generic single-database configuration.
|
||||||
70
alembic/env.py
Normal file
70
alembic/env.py
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
from __future__ import with_statement
|
||||||
|
from alembic import context
|
||||||
|
from sqlalchemy import engine_from_config, pool
|
||||||
|
from logging.config import fileConfig
|
||||||
|
|
||||||
|
# this is the Alembic Config object, which provides
|
||||||
|
# access to the values within the .ini file in use.
|
||||||
|
config = context.config
|
||||||
|
|
||||||
|
# Interpret the config file for Python logging.
|
||||||
|
# This line sets up loggers basically.
|
||||||
|
fileConfig(config.config_file_name)
|
||||||
|
|
||||||
|
# add your model's MetaData object here
|
||||||
|
# for 'autogenerate' support
|
||||||
|
# from myapp import mymodel
|
||||||
|
# target_metadata = mymodel.Base.metadata
|
||||||
|
target_metadata = None
|
||||||
|
|
||||||
|
# other values from the config, defined by the needs of env.py,
|
||||||
|
# can be acquired:
|
||||||
|
# my_important_option = config.get_main_option("my_important_option")
|
||||||
|
# ... etc.
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_offline():
|
||||||
|
"""Run migrations in 'offline' mode.
|
||||||
|
|
||||||
|
This configures the context with just a URL
|
||||||
|
and not an Engine, though an Engine is acceptable
|
||||||
|
here as well. By skipping the Engine creation
|
||||||
|
we don't even need a DBAPI to be available.
|
||||||
|
|
||||||
|
Calls to context.execute() here emit the given string to the
|
||||||
|
script output.
|
||||||
|
|
||||||
|
"""
|
||||||
|
url = config.get_main_option("sqlalchemy.url")
|
||||||
|
context.configure(
|
||||||
|
url=url, target_metadata=target_metadata, literal_binds=True)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
|
||||||
|
def run_migrations_online():
|
||||||
|
"""Run migrations in 'online' mode.
|
||||||
|
|
||||||
|
In this scenario we need to create an Engine
|
||||||
|
and associate a connection with the context.
|
||||||
|
|
||||||
|
"""
|
||||||
|
connectable = engine_from_config(
|
||||||
|
config.get_section(config.config_ini_section),
|
||||||
|
prefix='sqlalchemy.',
|
||||||
|
poolclass=pool.NullPool)
|
||||||
|
|
||||||
|
with connectable.connect() as connection:
|
||||||
|
context.configure(
|
||||||
|
connection=connection,
|
||||||
|
target_metadata=target_metadata
|
||||||
|
)
|
||||||
|
|
||||||
|
with context.begin_transaction():
|
||||||
|
context.run_migrations()
|
||||||
|
|
||||||
|
if context.is_offline_mode():
|
||||||
|
run_migrations_offline()
|
||||||
|
else:
|
||||||
|
run_migrations_online()
|
||||||
24
alembic/script.py.mako
Normal file
24
alembic/script.py.mako
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
"""${message}
|
||||||
|
|
||||||
|
Revision ID: ${up_revision}
|
||||||
|
Revises: ${down_revision | comma,n}
|
||||||
|
Create Date: ${create_date}
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
${imports if imports else ""}
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = ${repr(up_revision)}
|
||||||
|
down_revision = ${repr(down_revision)}
|
||||||
|
branch_labels = ${repr(branch_labels)}
|
||||||
|
depends_on = ${repr(depends_on)}
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
${upgrades if upgrades else "pass"}
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
${downgrades if downgrades else "pass"}
|
||||||
33
alembic/versions/1_users.py
Normal file
33
alembic/versions/1_users.py
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
"""Пользователи
|
||||||
|
|
||||||
|
Revision ID: 1d212513b25f
|
||||||
|
Revises:
|
||||||
|
Create Date: 2018-02-25 14:09:43.942507
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '1d212513b25f'
|
||||||
|
down_revision = None
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# Пользователи
|
||||||
|
op.create_table('user',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False, unique=True),
|
||||||
|
sa.Column('password', sa.String(), nullable=True),
|
||||||
|
sa.Column('disabled', sa.Boolean(), nullable=True),
|
||||||
|
sa.Column('created', sa.DateTime(), nullable=True),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('name')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_table("user")
|
||||||
29
alembic/versions/2_tag.py
Normal file
29
alembic/versions/2_tag.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
"""Tag
|
||||||
|
|
||||||
|
Revision ID: 61ede83f5cd4
|
||||||
|
Revises: 1d212513b25f
|
||||||
|
Create Date: 2018-02-25 14:10:08.770210
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '61ede83f5cd4'
|
||||||
|
down_revision = '1d212513b25f'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# Объекты доступа: процедура и содержащий процедуру модуль
|
||||||
|
op.create_table('tag',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('name', sa.String(), nullable=False, unique=True),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_table("tag")
|
||||||
46
alembic/versions/3_page.py
Normal file
46
alembic/versions/3_page.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: da6bc5b8d253
|
||||||
|
Revises: 61ede83f5cd4
|
||||||
|
Create Date: 2020-08-19 04:51:11.868138
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'da6bc5b8d253'
|
||||||
|
down_revision = '61ede83f5cd4'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# Статьи
|
||||||
|
op.create_table('page',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('title', sa.String(), nullable=True),
|
||||||
|
sa.Column('body', sa.String(), nullable=True),
|
||||||
|
sa.Column('created', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('updated', sa.DateTime(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
|
||||||
|
# Теги к заметкам
|
||||||
|
op.create_table('tagpage',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('page_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('tag_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('created', sa.DateTime(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['page_id'], ['page.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['tag_id'], ['tag.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_table("tagpage")
|
||||||
|
op.drop_table("page")
|
||||||
45
alembic/versions/4_note.py
Normal file
45
alembic/versions/4_note.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
"""empty message
|
||||||
|
|
||||||
|
Revision ID: df6187181c66
|
||||||
|
Revises: da6bc5b8d253
|
||||||
|
Create Date: 2020-08-19 04:51:24.137270
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = 'df6187181c66'
|
||||||
|
down_revision = 'da6bc5b8d253'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# Заметка
|
||||||
|
op.create_table('note',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('user_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('title', sa.String(), nullable=True),
|
||||||
|
sa.Column('body', sa.String(), nullable=True),
|
||||||
|
sa.Column('created', sa.DateTime(), nullable=True),
|
||||||
|
sa.Column('updated', sa.DateTime(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
|
||||||
|
# Теги к заметкам
|
||||||
|
op.create_table('tagnote',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('note_id', sa.Integer(), nullable=True),
|
||||||
|
sa.Column('tag_id', sa.Integer(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['note_id'], ['note.id'], ),
|
||||||
|
sa.ForeignKeyConstraint(['tag_id'], ['tag.id'], ),
|
||||||
|
sa.PrimaryKeyConstraint('id')
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_table("tagnote")
|
||||||
|
op.drop_table("note")
|
||||||
19
deploy/Dockerfile
Normal file
19
deploy/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
FROM python:3.8
|
||||||
|
|
||||||
|
RUN groupadd user
|
||||||
|
RUN useradd -m -d /home/user -s /bin/bash -g user -m user
|
||||||
|
|
||||||
|
WORKDIR /home/user
|
||||||
|
COPY deploy/requirements.txt /tmp/
|
||||||
|
RUN pip install -r /tmp/requirements.txt
|
||||||
|
|
||||||
|
USER user:user
|
||||||
|
|
||||||
|
RUN mkdir -p ~/data/files
|
||||||
|
RUN touch ~/data/myapp.log
|
||||||
|
|
||||||
|
COPY --chown=user:user . ./
|
||||||
|
|
||||||
|
# EXPOSE 8000
|
||||||
|
|
||||||
|
CMD ["gunicorn", "-w", "4", "--bind=0.0.0.0", "myapp:app"]
|
||||||
10
deploy/nginx/app.conf
Normal file
10
deploy/nginx/app.conf
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
access_log /var/log/nginx/myapp-access.log;
|
||||||
|
error_log /var/log/nginx/myapp-error.log;
|
||||||
|
location / {
|
||||||
|
include proxy_params;
|
||||||
|
proxy_pass http://app:8000/;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name myapp www.myapp;
|
|
||||||
access_log /var/log/nginx/myapp-access.log mainproxy;
|
|
||||||
error_log /var/log/nginx/myapp-error.log info;
|
|
||||||
location / {
|
|
||||||
include uwsgi_params;
|
|
||||||
uwsgi_pass unix:///run/uwsgi/myapp.sock;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
3
deploy/nginx/proxy_params
Normal file
3
deploy/nginx/proxy_params
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
wtforms
|
Flask==1.1.2
|
||||||
flask
|
Flask-JSONRPC==1.1.0
|
||||||
sqlalchemy
|
alembic==1.4.2
|
||||||
sqlalchemy-utils
|
SQLAlchemy==1.3.17
|
||||||
alembic
|
SQLAlchemy-Utils==0.36.6
|
||||||
celery
|
flake8==3.8.3
|
||||||
|
gunicorn==20.0.4
|
||||||
|
psycopg2-binary==2.8.5
|
||||||
|
|||||||
43
docker-compose.yml
Normal file
43
docker-compose.yml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: ./
|
||||||
|
dockerfile: ./deploy/Dockerfile
|
||||||
|
volumes:
|
||||||
|
- data:/home/app/data
|
||||||
|
- config:/home/app/config
|
||||||
|
links:
|
||||||
|
- db
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
restart:
|
||||||
|
always
|
||||||
|
command: ./entrypoint.sh
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:latest
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- ./data/db:/var/lib/postgresql/data
|
||||||
|
- ./init.sql:/docker-entrypoint-initdb.d/10-init.sql
|
||||||
|
env_file:
|
||||||
|
- .env.db
|
||||||
|
|
||||||
|
nginx:
|
||||||
|
image: nginx:latest
|
||||||
|
restart: always
|
||||||
|
links:
|
||||||
|
- app
|
||||||
|
ports:
|
||||||
|
- "8000:80"
|
||||||
|
volumes:
|
||||||
|
- ./logs:/var/log/nginx
|
||||||
|
- ./deploy/nginx/proxy_params:/etc/nginx/proxy_params
|
||||||
|
- ./deploy/nginx/app.conf:/etc/nginx/conf.d/app.conf
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
db:
|
||||||
|
data:
|
||||||
|
config:
|
||||||
4
entrypoint.sh
Executable file
4
entrypoint.sh
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
alembic upgrade head
|
||||||
|
gunicorn -w 4 --bind=0.0.0.0 myapp:app
|
||||||
@@ -50,7 +50,6 @@ class TagNote(Base):
|
|||||||
id = Column(Integer, primary_key=True)
|
id = Column(Integer, primary_key=True)
|
||||||
note_id = Column(Integer, ForeignKey('note.id'))
|
note_id = Column(Integer, ForeignKey('note.id'))
|
||||||
tag_id = Column(Integer, ForeignKey('tag.id'))
|
tag_id = Column(Integer, ForeignKey('tag.id'))
|
||||||
created = Column(DateTime) # Дата создания
|
|
||||||
|
|
||||||
# Связи
|
# Связи
|
||||||
note = relationship(
|
note = relationship(
|
||||||
@@ -69,7 +68,6 @@ class TagNote(Base):
|
|||||||
assert type(tag).__name__ == 'Tag', 'Не передан объект Tag'
|
assert type(tag).__name__ == 'Tag', 'Не передан объект Tag'
|
||||||
self.note_id = note.id
|
self.note_id = note.id
|
||||||
self.tag_id = tag.id
|
self.tag_id = tag.id
|
||||||
self.created = datetime.datetime.now()
|
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<TagNote('%s')>" % (self.id)
|
return "<TagNote('%s')>" % (self.id)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ __author__ = 'RemiZOffAlex'
|
|||||||
__email__ = 'remizoffalex@mail.ru'
|
__email__ = 'remizoffalex@mail.ru'
|
||||||
__url__ = 'https://remizoffalex.ru/'
|
__url__ = 'https://remizoffalex.ru/'
|
||||||
|
|
||||||
|
import string
|
||||||
|
|
||||||
from flask import session
|
from flask import session
|
||||||
|
|
||||||
from . import jsonrpc
|
from . import jsonrpc
|
||||||
@@ -25,3 +27,34 @@ def login(username: str, password: str) -> bool:
|
|||||||
session['logged_in'] = True
|
session['logged_in'] = True
|
||||||
session['user_id'] = user.id
|
session['user_id'] = user.id
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@jsonrpc.method('login.register')
|
||||||
|
def login_register(username: str, password: str) -> bool:
|
||||||
|
"""Регистрация
|
||||||
|
"""
|
||||||
|
if len(username) < 4 or len(username) > 25:
|
||||||
|
raise ValueError('Длина логина от 4 до 25 символов')
|
||||||
|
if len(password) < 4 or len(password) > 150:
|
||||||
|
raise ValueError('Длина пароля от 4 до 150 символов')
|
||||||
|
for char in username:
|
||||||
|
if char not in string.ascii_letters + string.digits:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
user = models.db_session.query(
|
||||||
|
models.User
|
||||||
|
).filter(
|
||||||
|
models.User.name == username
|
||||||
|
).first()
|
||||||
|
if user:
|
||||||
|
raise ValueError('Пользователь с таким логином уже существует')
|
||||||
|
|
||||||
|
newuser = models.User(username)
|
||||||
|
newuser.password = lib.get_hash_password(
|
||||||
|
password,
|
||||||
|
app.config['SECRET_KEY']
|
||||||
|
)
|
||||||
|
models.db_session.add(newuser)
|
||||||
|
models.db_session.commit()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
|
<a class="btn btn-outline-secondary" href="/register">Регистрация</a>
|
||||||
<button class="btn btn-outline-success float-right" type="submit" v-on:click="login">Войти</button>
|
<button class="btn btn-outline-success float-right" type="submit" v-on:click="login">Войти</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
90
myapp/ns_login/templates/register.html
Normal file
90
myapp/ns_login/templates/register.html
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
{% extends "skeleton.html" %}
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 ml-md-auto mr-md-auto">
|
||||||
|
|
||||||
|
<h3>Регистрация</h3>
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
<div class="row mt-3" v-if="error">
|
||||||
|
<div class="col text-danger" v-html="error.message"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<i class="input-group-text"><i class="fa fa-user"></i></i>
|
||||||
|
</div>
|
||||||
|
<input class="form-control" placeholder="Логин" type="text" id="username" v-model="username">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<i class="input-group-text"><i class="fa fa-lock"></i></i>
|
||||||
|
</div>
|
||||||
|
<input class="form-control" placeholder="Пароль" type="password" id="password" v-model="password">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="input-group">
|
||||||
|
<div class="input-group-prepend">
|
||||||
|
<i class="input-group-text"><i class="fa fa-lock"></i></i>
|
||||||
|
</div>
|
||||||
|
<input class="form-control" placeholder="Повтор пароля" type="password" id="repeat" v-model="repeat">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn btn-outline-success float-right" v-on:click="register">Зарегистрироваться</div>
|
||||||
|
<a class="btn btn-outline-secondary" href="/login">Вход</a>
|
||||||
|
|
||||||
|
</div> <!-- div class="col-md-6 ml-md-auto mr-md-auto" -->
|
||||||
|
</div> <!-- div class="row" -->
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block script %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
var app = new Vue({
|
||||||
|
el: '#app',
|
||||||
|
data: {
|
||||||
|
username: '',
|
||||||
|
password: '',
|
||||||
|
repeat: '',
|
||||||
|
error: null
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
register: function() {
|
||||||
|
let vm = this;
|
||||||
|
if (vm.password!=vm.repeat) {
|
||||||
|
vm.error = 'Пароли не совпадают';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
axios.post(
|
||||||
|
'/api',
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "login.register",
|
||||||
|
"params": {
|
||||||
|
"username": vm.username,
|
||||||
|
"password": vm.password
|
||||||
|
},
|
||||||
|
"id": 1
|
||||||
|
}
|
||||||
|
).then(
|
||||||
|
function(response) {
|
||||||
|
if ('result' in response.data) {
|
||||||
|
window.location.href = '/login';
|
||||||
|
} else if ('error' in response.data) {
|
||||||
|
vm.error = response.data['error'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
@@ -23,3 +23,12 @@ def logout():
|
|||||||
session.pop('logged_in', None)
|
session.pop('logged_in', None)
|
||||||
session.pop('user_id', None)
|
session.pop('user_id', None)
|
||||||
return redirect("/", code=302)
|
return redirect("/", code=302)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/register')
|
||||||
|
def register():
|
||||||
|
"""Регистрация нового пользователя
|
||||||
|
"""
|
||||||
|
pagedata = {}
|
||||||
|
body = render_template('register.html', pagedata=pagedata)
|
||||||
|
return body
|
||||||
|
|||||||
@@ -8,7 +8,8 @@ from .. import app, models
|
|||||||
|
|
||||||
|
|
||||||
def pages(page):
|
def pages(page):
|
||||||
"""Список статей"""
|
"""Список статей
|
||||||
|
"""
|
||||||
pagedata = {'title': 'Статьи - ' + app.config['TITLE']}
|
pagedata = {'title': 'Статьи - ' + app.config['TITLE']}
|
||||||
|
|
||||||
pagedata['pagination'] = {
|
pagedata['pagination'] = {
|
||||||
|
|||||||
@@ -8,8 +8,7 @@ from .. import app, models
|
|||||||
|
|
||||||
|
|
||||||
def pages(page):
|
def pages(page):
|
||||||
"""
|
"""Список статей
|
||||||
Список статей
|
|
||||||
"""
|
"""
|
||||||
pagedata = {'title': 'Статьи - ' + app.config['TITLE']}
|
pagedata = {'title': 'Статьи - ' + app.config['TITLE']}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col py-2">
|
<div class="col py-2">
|
||||||
|
|
||||||
<a class="btn btn-outline-secondary border-0" href="/"><i class="fa fa-home"></i></a>
|
<a class="btn btn-outline-secondary" href="/"><i class="fa fa-home"></i></a>
|
||||||
<a class="btn btn-outline-secondary border-0" href="/pages">Статьи</a>
|
<a class="btn btn-outline-secondary border-0" href="/pages">Статьи</a>
|
||||||
<a class="btn btn-outline-secondary border-0" href="/tags">Метки</a>
|
<a class="btn btn-outline-secondary border-0" href="/tags">Метки</a>
|
||||||
<a class="btn btn-outline-secondary border-0" href="/users">Пользователи</a>
|
<a class="btn btn-outline-secondary border-0" href="/users">Пользователи</a>
|
||||||
<a class="btn btn-outline-secondary border-0" href="/api/browse">API JSON-RPC</a>
|
<a class="btn btn-outline-secondary border-0" href="/api/browse">API JSON-RPC</a>
|
||||||
|
|
||||||
<a class="btn btn-outline-success border-0 float-right" href="/login"><i class="fa fa-sign-in"></i></a>
|
<a class="btn btn-outline-success float-right" href="/login"><i class="fa fa-sign-in"></i></a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col py-2">
|
<div class="col py-2">
|
||||||
|
|
||||||
<a class="btn btn-outline-secondary border-0" href="/"><i class="fa fa-home"></i></a>
|
<a class="btn btn-outline-secondary" href="/"><i class="fa fa-home"></i></a>
|
||||||
<a class="btn btn-outline-secondary border-0" href="/pages">Статьи</a>
|
<a class="btn btn-outline-secondary border-0" href="/pages">Статьи</a>
|
||||||
<a class="btn btn-outline-secondary border-0" href="/tags">Метки</a>
|
<a class="btn btn-outline-secondary border-0" href="/tags">Метки</a>
|
||||||
<a class="btn btn-outline-secondary border-0" href="/users">Пользователи</a>
|
<a class="btn btn-outline-secondary border-0" href="/users">Пользователи</a>
|
||||||
<a class="btn btn-outline-secondary border-0" href="/api/browse">API JSON-RPC</a>
|
<a class="btn btn-outline-secondary border-0" href="/api/browse">API JSON-RPC</a>
|
||||||
|
|
||||||
<div class="btn-group float-right">
|
<div class="btn-group float-right">
|
||||||
<a class="btn btn-outline-secondary border-0" href="/notes"><i class="fa fa-sticky-note-o"></i></a>
|
<a class="btn btn-outline-secondary" href="/notes"><i class="fa fa-sticky-note-o"></i></a>
|
||||||
<a class="btn btn-outline-secondary border-0" href="/profile"><i class="fa fa-user"></i></a>
|
<a class="btn btn-outline-secondary" href="/profile"><i class="fa fa-user"></i></a>
|
||||||
<a class="btn btn-outline-danger border-0" href="/logout"><i class="fa fa-sign-out"></i></a>
|
<a class="btn btn-outline-danger" href="/logout"><i class="fa fa-sign-out"></i></a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user