initial commit
This commit is contained in:
commit
3b3c9a9417
258 changed files with 20086 additions and 0 deletions
17
services/myblog/api_server/Dockerfile
Normal file
17
services/myblog/api_server/Dockerfile
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
FROM python:3-slim
|
||||
|
||||
RUN useradd -ms /bin/bash volgactf
|
||||
|
||||
RUN pip install --no-cache-dir quart nanoid requests flask_sqlalchemy aiohttp psycopg2-binary quart-cors
|
||||
|
||||
WORKDIR /home/volgactf/dist
|
||||
|
||||
COPY . ./
|
||||
|
||||
RUN chown -R volgactf:volgactf /home/volgactf/dist
|
||||
|
||||
USER volgactf
|
||||
|
||||
|
||||
CMD python routes.py
|
||||
|
||||
12
services/myblog/api_server/app/__init__.py
Normal file
12
services/myblog/api_server/app/__init__.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from quart import Quart
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
import os, re
|
||||
from quart_cors import cors, route_cors
|
||||
|
||||
app = Quart(__name__)
|
||||
app = cors(app, allow_origin=re.compile(r"http:\/\/.*:3000"), allow_credentials=True)#cors(app, allow_origin="http://10.50.20.4:3000", allow_credentials=True)
|
||||
app.config["REDIRECT_SERVER"] = os.getenv('REDIRECT_SERVER') or "127.0.0.1:8080"#"10.50.20.7:8080"
|
||||
app.config["CONTENT_SERVER"] = os.getenv('CONTENT_SERVER') or "127.0.0.1:13379"#"10.50.20.6:13379"
|
||||
app.config["SQLALCHEMY_DATABASE_URI"] = os.getenv('SQLALCHEMY_DATABASE_URI') or 'sqlite:///data.db'
|
||||
app.config["SECRET_KEY"] = 'hack_me'
|
||||
db = SQLAlchemy(app)
|
||||
45
services/myblog/api_server/app/models.py
Normal file
45
services/myblog/api_server/app/models.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
from nanoid import generate
|
||||
from . import db
|
||||
|
||||
class User(db.Model):
|
||||
def __init__(self, username, blog_url):
|
||||
self.username = username
|
||||
self.blog_url = blog_url
|
||||
__tablename__ = 'users'
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
username = db.Column(db.Text, unique=True)
|
||||
password = db.Column(db.Text)
|
||||
blog_url = db.Column(db.Text, db.ForeignKey('blogs.url', ondelete='CASCADE'), unique=True)
|
||||
blog = db.relationship('Blog', backref='blog_user', cascade="all,delete")
|
||||
def set_password(self, password):
|
||||
self.password = generate_password_hash(password)
|
||||
|
||||
def check_password(self, password):
|
||||
return check_password_hash(self.password, password)
|
||||
|
||||
class Blog(db.Model):
|
||||
def __init__(self, url, is_private=False):
|
||||
self.url = url
|
||||
self.is_private = is_private
|
||||
__tablename__ = "blogs"
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
url = db.Column(db.Text, unique=True)
|
||||
is_private = db.Column(db.Boolean, default=False) #TODO this is must be private (VULN)
|
||||
user = db.relationship('User', backref='blog_user', cascade="all,delete", lazy='dynamic')
|
||||
posts = db.relationship("Post", cascade="all,delete")
|
||||
|
||||
|
||||
#TODO дописать стуруктуру БД
|
||||
|
||||
class Post(db.Model):
|
||||
def __init__(self, title, body, blog_url):
|
||||
self.title = title
|
||||
self.body = body
|
||||
self.blog_url = blog_url
|
||||
__tablename__ = "posts"
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
title = db.Column(db.String)
|
||||
body = db.Column(db.String)
|
||||
blog_url = db.Column(db.String, db.ForeignKey("blogs.url"))
|
||||
blog = db.relationship("Blog", cascade="all,delete")
|
||||
11
services/myblog/api_server/init_db.py
Normal file
11
services/myblog/api_server/init_db.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
from app import app, db
|
||||
from flask import g, current_app
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
|
||||
if __name__ == "__main__":
|
||||
def get_db():
|
||||
if 'db' not in g:
|
||||
g.db = SQLAlchemy(current_app)
|
||||
return g.db
|
||||
get_db().create_all()
|
||||
#db.create_all()
|
||||
255
services/myblog/api_server/routes.py
Normal file
255
services/myblog/api_server/routes.py
Normal file
|
|
@ -0,0 +1,255 @@
|
|||
from quart import request, Response, render_template, session, redirect, jsonify, abort, current_app, url_for
|
||||
import aiohttp
|
||||
from app import app, db
|
||||
from app.models import User, Blog, Post
|
||||
from nanoid import generate
|
||||
from functools import wraps
|
||||
import json
|
||||
from quart_cors import route_cors
|
||||
|
||||
@app.before_first_request
|
||||
def create_tables():
|
||||
db.create_all()
|
||||
|
||||
def user_cook(f):
|
||||
@wraps(f)
|
||||
async def decorated_function(*args, **kwargs):
|
||||
if session.get('username'):
|
||||
return await current_app.ensure_async(f)(*args, **kwargs)
|
||||
else:
|
||||
return abort(403)
|
||||
return decorated_function
|
||||
|
||||
@app.route('/health_check')
|
||||
#@route_cors()
|
||||
async def health_check():
|
||||
status1, _, _, _ = await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/health_check")
|
||||
status2, _, _, _ = await get_request(f"http://{app.config.get('CONTENT_SERVER')}/health_check")
|
||||
if status1 == 200 and status2 == 200:
|
||||
return "OK"
|
||||
else:
|
||||
return "Not OK", 500
|
||||
|
||||
@app.route('/health_check2')
|
||||
#@route_cors()
|
||||
async def health_check2():
|
||||
return "OK"
|
||||
|
||||
@app.route('/image/<path>')
|
||||
#@route_cors()
|
||||
async def get_image(path): # put application's code here
|
||||
#if x.get(""): #if valid image (example 404, 405, corrupted_image)
|
||||
#...
|
||||
#else:
|
||||
|
||||
path2 = request.args.get("another") #путь до картинки (логика хранения картинки как в CDN (начало пути - начало хеша).
|
||||
filename = request.args.get("filename")
|
||||
content_server = app.config.get("CONTENT_SERVER")
|
||||
if path2 == 'secrets' or path == 'secrets':
|
||||
return abort(403)
|
||||
status_code, json_data, data_data, headers = await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/{path}/{content_server}?filename={filename}")
|
||||
if status_code == 200:
|
||||
if len(filename.split('.')) > 0 and filename.split('.')[-1] == 'png':
|
||||
content_type = "Content-Type: image/png"
|
||||
return Response(
|
||||
response=data_data,
|
||||
content_type=content_type,
|
||||
status=status_code
|
||||
)
|
||||
else:
|
||||
status_code, json_data, data_data, headers = await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/{path2}/{content_server}?filename={filename}")
|
||||
content_type = headers.get("Content-Type")
|
||||
if len(filename.split('.')) > 0 and filename.split('.')[-1] == 'png':
|
||||
content_type = "Content-Type: image/png"
|
||||
return Response(
|
||||
response=data_data,
|
||||
content_type=content_type,
|
||||
status=status_code
|
||||
)
|
||||
|
||||
async def post_request(url, headers={}, json_inp=None, data=None, cookies=None):
|
||||
async with aiohttp.ClientSession(cookies=cookies, skip_auto_headers={"User-Agent"}) as session:
|
||||
async with session.post(url, headers=headers, json=json_inp, data=data) as r:
|
||||
data = ""
|
||||
json_data = ""
|
||||
if hasattr(r, "data"):
|
||||
data = await r.data
|
||||
if r.content_type == 'application/json':
|
||||
json_data = await r.json()
|
||||
return r.status, json_data, data, r.headers
|
||||
|
||||
async def get_request(url, headers={}, cookies={}):
|
||||
async with aiohttp.ClientSession(cookies=cookies, skip_auto_headers={"User-Agent"}) as session:
|
||||
async with session.get(url, headers=headers) as r:
|
||||
data = ""
|
||||
json_data = ""
|
||||
if r.content_type == 'application/json':
|
||||
json_data = await r.json()
|
||||
return r.status, json_data, data, r.headers
|
||||
if hasattr(r, "data"):
|
||||
data = r.data
|
||||
elif hasattr(r, "content"):
|
||||
data = await r.content.read()
|
||||
return r.status, json_data, data, r.headers
|
||||
|
||||
|
||||
@app.route('/file/get/<path>', methods=['GET'])
|
||||
#@route_cors()
|
||||
@user_cook
|
||||
async def get_file(path): # put application's code here
|
||||
async def check_access(username, filename):
|
||||
status_code, _, _, _ = await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/check_access?filename={filename}&username={username}")
|
||||
if status_code == 200:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
filename = request.args.get("filename")
|
||||
content_server = app.config.get("CONTENT_SERVER")
|
||||
if not await check_access(session.get("username"), filename):
|
||||
return abort(403)
|
||||
status_code, json_data, data_data, header = await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/{path}/{content_server}?filename={filename}")
|
||||
content_type = header.get("Content-Type")
|
||||
if len(filename.split('.')) > 0 and filename.split('.')[-1] == 'png':
|
||||
content_type = "Content-Type: image/png"
|
||||
return Response(
|
||||
response=data_data,
|
||||
content_type=content_type,
|
||||
status=status_code
|
||||
)
|
||||
|
||||
@app.route('/file/list')
|
||||
#@route_cors()
|
||||
async def file_list():
|
||||
content_server = app.config.get("CONTENT_SERVER")
|
||||
path = request.args.get('path')
|
||||
status_code, json_data, _, headers = await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/file_list/{path}/{content_server}")
|
||||
return Response(
|
||||
response=json.dumps(json_data),
|
||||
content_type=headers.get('Content-Type'),
|
||||
status=status_code)
|
||||
|
||||
|
||||
|
||||
@app.route('/file/upload', methods=['POST'])
|
||||
#@route_cors()
|
||||
@user_cook
|
||||
async def upload_image():
|
||||
if request.method == 'POST':
|
||||
if 'file' not in await request.files:
|
||||
return abort(403)
|
||||
content_server = app.config.get("CONTENT_SERVER")
|
||||
path = request.args.get("path")
|
||||
username = session.get("username")
|
||||
form_data = aiohttp.FormData()
|
||||
file_bytes = (await request.files).get('file').stream.read()
|
||||
filename = (await request.files).get("file").filename
|
||||
form_data.add_field(name='file', value=file_bytes , filename=filename)
|
||||
form_data.add_field(name="username", value=username)
|
||||
form_data.add_field(name="filename", value=filename)
|
||||
status_code, json_data, data_data, headers = await post_request(f"http://{app.config.get('REDIRECT_SERVER')}/{path}/{content_server}",
|
||||
data=form_data)
|
||||
|
||||
return Response(
|
||||
response=json.dumps(json_data),
|
||||
content_type=headers.get('Content-Type'),
|
||||
status=status_code
|
||||
)
|
||||
|
||||
@app.route('/api/auth/sign_up', methods=["POST"])
|
||||
#@route_cors()
|
||||
async def sign_up():
|
||||
username = (await request.json).get("username")
|
||||
password = (await request.json).get("password")
|
||||
is_private = (await request.json).get("is_private")
|
||||
if username and password:
|
||||
url_id = generate('1234567890abcdef', 12)
|
||||
user = User(username=username, blog_url=url_id)
|
||||
user.set_password(password)
|
||||
blog = Blog(url_id, is_private)
|
||||
db.session.add(user)
|
||||
db.session.add(blog)
|
||||
db.session.commit()
|
||||
session["username"] = user.username
|
||||
return jsonify({"Status": "OK"})
|
||||
else:
|
||||
return abort(403)
|
||||
|
||||
|
||||
|
||||
@app.route('/api/auth/sign_in', methods=["POST"])
|
||||
async def sign_in():
|
||||
username = (await request.json).get("username")
|
||||
password = (await request.json).get("password")
|
||||
if username and password:
|
||||
user = User.query.filter_by(username=username).first()
|
||||
if user is None or not user.check_password(password):
|
||||
return jsonify({"Result": "Bad creads"}), 400
|
||||
else:
|
||||
session["username"] = user.username
|
||||
return jsonify({"Result": "OK"})
|
||||
|
||||
@app.route('/api/self_delete')
|
||||
#@route_cors()
|
||||
@user_cook
|
||||
async def self_remove_acc():
|
||||
username = session.get("username")
|
||||
user = User.query.filter_by(username=username).first()
|
||||
db.session.delete(user)
|
||||
db.session.commit()
|
||||
await get_request(f"http://{app.config.get('REDIRECT_SERVER')}/delete/{username}/{app.config.get('CONTENT_SERVER')}")
|
||||
return jsonify({"result": "OK"})
|
||||
|
||||
@app.route('/api/blog')
|
||||
#@route_cors()
|
||||
@user_cook
|
||||
async def get_my_blog():
|
||||
user = User.query.filter_by(username=session.get("username")).first()
|
||||
blog = Blog.query.filter_by(url=user.blog_url).first()
|
||||
return jsonify({"url": blog.url, "is_private": blog.is_private})
|
||||
|
||||
@app.route('/api/blogs')
|
||||
#@route_cors()
|
||||
async def get_blogs():
|
||||
blogs = Blog.query.with_entities(Blog.url, Blog.is_private).all()
|
||||
blogs_list = [{"url": i.url, "is_private": i.is_private} for i in blogs]
|
||||
return jsonify(blogs_list)
|
||||
|
||||
|
||||
@app.route('/api/blog/<url_id>')
|
||||
#@route_cors()
|
||||
@user_cook
|
||||
async def blog_get_posts(url_id):
|
||||
blog = Blog.query.filter_by(url=url_id).first()
|
||||
if blog:
|
||||
if blog.posts:
|
||||
return jsonify({"posts": list(map(lambda x: {"id":x.id, "title":x.title}, blog.posts))})
|
||||
else:
|
||||
return jsonify({"error": "?"})
|
||||
else:
|
||||
return jsonify({"error": "?"})
|
||||
|
||||
@app.route('/api/blog/<url_id>/create_post', methods=["POST"])
|
||||
#@route_cors()
|
||||
@user_cook
|
||||
async def blog_create_post(url_id):
|
||||
blog = Blog.query.filter_by(url=url_id).first()
|
||||
title = (await request.json).get("title")
|
||||
body = (await request.json).get("body")
|
||||
post = Post(title=title, body=body, blog_url=blog.url)
|
||||
db.session.add(post)
|
||||
db.session.commit()
|
||||
this_post = Post.query.filter_by(title=title, body=body).all()[-1]
|
||||
return jsonify({"Result": "OK", "post_id": this_post.id})
|
||||
|
||||
|
||||
@app.route('/api/blog/<url_id>/post/<int:post_id>')
|
||||
#@route_cors()
|
||||
@user_cook
|
||||
async def blog_read_post(url_id, post_id):
|
||||
post = Post.query.filter_by(blog_url=url_id, id=post_id).first()
|
||||
return jsonify({"title": post.title, "body": post.body})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
app.run(port=13377, host="0.0.0.0", debug=False)
|
||||
Loading…
Add table
Add a link
Reference in a new issue