summaryrefslogtreecommitdiffstats
path: root/frest
diff options
context:
space:
mode:
authorSanto Cariotti <dcariotti24@gmail.com>2020-04-06 19:54:53 +0000
committerSanto Cariotti <dcariotti24@gmail.com>2020-04-06 19:54:53 +0000
commit3a2246e26e9febe3c15e2ddc1e7e6f320f86fe15 (patch)
tree9cbd95771d38dfd66bedb4a447470391d34a68eb /frest
parent6188a952974b3e268936beb1027ea58fbfaa67aa (diff)
chore: move package in frest folder
Diffstat (limited to 'frest')
-rw-r--r--frest/__init__.py4
-rw-r--r--frest/app.py57
-rw-r--r--frest/auth/__init__.py0
-rw-r--r--frest/auth/forms.py10
-rw-r--r--frest/auth/models.py52
-rw-r--r--frest/auth/routes.py183
-rw-r--r--frest/database.py4
-rw-r--r--frest/decorators.py40
-rw-r--r--frest/forms.py20
-rw-r--r--frest/mail.py11
-rw-r--r--frest/manage/__init__.py0
-rw-r--r--frest/manage/bcolors.py17
-rw-r--r--frest/manage/utils.py228
-rw-r--r--frest/templates/form.txt11
-rw-r--r--frest/templates/models.txt26
-rw-r--r--frest/templates/routes.txt88
-rw-r--r--frest/utils.py44
-rw-r--r--frest/wsgi.py4
18 files changed, 799 insertions, 0 deletions
diff --git a/frest/__init__.py b/frest/__init__.py
new file mode 100644
index 0000000..dda6153
--- /dev/null
+++ b/frest/__init__.py
@@ -0,0 +1,4 @@
+from .forms import *
+from .utils import *
+from .mail import *
+from .database import *
diff --git a/frest/app.py b/frest/app.py
new file mode 100644
index 0000000..ca46277
--- /dev/null
+++ b/frest/app.py
@@ -0,0 +1,57 @@
+from flask import Flask
+from auth.routes import api as api_users
+from flask import make_response, jsonify
+from database import db
+from database import config as db_config
+from mail import mail
+from mail import config as mail_config
+from flask_sqlalchemy import SQLAlchemy
+from utils import http_call
+from flask_cors import CORS
+import os
+
+app = Flask(__name__)
+app.config["SQLALCHEMY_DATABASE_URI"] = db_config["DATABASE_URI"]
+app.config["DEBUG"] = os.getenv("FREST_DEBUG", True)
+app.config["CORS_HEADERS"] = "Content-Type"
+app.config["MAIL_SERVER"] = mail_config["SERVER"]
+app.config["MAIL_PORT"] = mail_config["PORT"]
+app.config["MAIL_USE_TLS"] = mail_config["USE_TLS"]
+app.config["MAIL_USERNAME"] = mail_config["USERNAME"]
+app.config["MAIL_DEFAULT_SENDER"] = mail_config["DEFAULT_SENDER"]
+app.config["MAIL_PASSWORD"] = mail_config["PASSWORD"]
+
+cors = CORS(app, resources={r"/.*": {"origins": "*"}})
+db.app = app
+db.init_app(app)
+mail.init_app(app)
+app.register_blueprint(api_users)
+
+
+@app.errorhandler(404)
+def not_found(error):
+ return http_call("Not found", 404)
+
+
+@app.errorhandler(400)
+def bad_request(error):
+ return http_call("Bad request", 400)
+
+
+@app.errorhandler(405)
+def method_not_allowed(error):
+ return http_call("Method not allowed", 405)
+
+
+@app.errorhandler(403)
+def forbiddend(error):
+ return http_call("Forbidden", 403)
+
+
+@app.errorhandler(500)
+def internal(error):
+ return http_call("Internal error", 500)
+
+
+if __name__ == "__main__":
+ app.run(host="0.0.0.0", port=5000)
diff --git a/frest/auth/__init__.py b/frest/auth/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/frest/auth/__init__.py
diff --git a/frest/auth/forms.py b/frest/auth/forms.py
new file mode 100644
index 0000000..abc2f49
--- /dev/null
+++ b/frest/auth/forms.py
@@ -0,0 +1,10 @@
+from .models import User
+from forms import ModelForm
+
+
+class UserForm(ModelForm):
+ model = User
+
+ def __init__(self, data):
+ super().__init__(self.model)
+ self.data = data
diff --git a/frest/auth/models.py b/frest/auth/models.py
new file mode 100644
index 0000000..ea79def
--- /dev/null
+++ b/frest/auth/models.py
@@ -0,0 +1,52 @@
+from database import db
+from datetime import datetime
+import string
+import random
+from hashlib import sha256
+from pytz import timezone
+import os
+
+
+def generate_token():
+ chars = string.ascii_uppercase + string.ascii_lowercase + string.digits
+ return "".join(random.choice(chars) for _ in range(18))
+
+
+class User(db.Model):
+ userId = db.Column(db.Integer, primary_key=True)
+ email = db.Column(db.String(30))
+ password = db.Column(db.String(30))
+ is_admin = db.Column(db.Boolean, default=False)
+ name = db.Column(db.String(30))
+ created_at = db.Column(db.DateTime)
+
+ def __init__(self, **kwargs):
+ self.email = kwargs.get("email")
+ psw_hash = sha256(kwargs.get("password").encode())
+ self.password = psw_hash.hexdigest()
+ self.name = kwargs.get("name")
+ self.is_admin = kwargs.get("is_admin")
+ self.created_at = datetime.now(
+ timezone(os.getenv("FREST_TIMEZONE", "Europe/Rome"))
+ )
+
+ def __repr__(self):
+ return f"<User '{self.userId}'>"
+
+
+class Token(db.Model):
+ tokenId = db.Column(db.Integer, primary_key=True)
+ string = db.Column(db.String(20))
+ created_at = db.Column(db.DateTime)
+ expired = db.Column(db.Boolean)
+ user_id = db.Column(db.Integer, db.ForeignKey("user.userId"), nullable=False)
+ user = db.relationship("User", backref=db.backref("tokens", lazy=True))
+
+ def __init__(self, user):
+ self.user = user
+ self.string = f"{generate_token()}=="
+ self.created_at = datetime.utcnow()
+ self.expired = False
+
+ def __repr__(self):
+ return f"<Token '{self.string}'>"
diff --git a/frest/auth/routes.py b/frest/auth/routes.py
new file mode 100644
index 0000000..27f00ec
--- /dev/null
+++ b/frest/auth/routes.py
@@ -0,0 +1,183 @@
+from flask import Blueprint, request, abort
+from utils import http_call, model_serialize
+from decorators import check_token, admin_required
+from .models import User, Token
+from .forms import UserForm
+from database import db
+from hashlib import sha256
+from sqlalchemy import desc
+
+api = Blueprint("users", __name__)
+
+
+@api.route("/api/login", methods=["POST"])
+def login():
+ if not request.json:
+ abort(400)
+
+ data = request.json
+
+ auth = request.headers.get("Authorization")
+ if auth:
+ t = Token.query.filter_by(string=auth).first()
+ if not t:
+ abort(404)
+
+ if t.user.is_admin:
+ return http_call(
+ {"userId": t.user.userId, "login": True, "token": t.string}, 200
+ )
+ else:
+ abort(403)
+
+ if "email" in data and "password" in data:
+ psw_hash = sha256(data["password"].encode())
+ data["password"] = psw_hash.hexdigest()
+ u = User.query.filter_by(email=data["email"], password=data["password"]).first()
+
+ if not u:
+ abort(404)
+
+ if "is_admin" in data:
+ if u.is_admin == 0:
+ abort(403)
+
+ last_token = (
+ Token.query.filter_by(user=u).order_by(desc(Token.tokenId)).all()[-1]
+ )
+ last_token.expired = True
+
+ t = Token(user=u)
+
+ db.session.add(t)
+ db.session.commit()
+
+ return http_call({"userId": u.userId, "login": True, "token": t.string}, 200)
+
+ abort(404)
+
+
+@api.route("/api/user/hash_password", methods=["GET"])
+def hash_password_exists():
+ data = request.args
+ if not data.get("hash_password"):
+ abort(400)
+
+ if User.query.filter_by(password=data["hash_password"]):
+ return http_call({}, 200)
+
+ return http_call({}, 404)
+
+
+@api.route("/api/user/new-password/<alias>", methods=["PUT"])
+def new_user_password(alias):
+ data = request.json
+ if not data.get("password"):
+ abort(400)
+
+ u = User.query.filter_by(password=alias).first()
+
+ if not u:
+ abort(404)
+
+ u.password = sha256(data["password"].encode()).hexdigest()
+ db.session.commit()
+
+ return http_call({}, 200)
+
+
+@api.route("/api/user", methods=["POST"])
+def new_user():
+ if not request.json:
+ abort(400)
+
+ form = UserForm(request.json)
+
+ if not form.get("is_admin") or form.is_valid():
+ if User.query.filter_by(email=form.get("email")).first():
+ abort(400)
+
+ u = User(
+ email=form.get("email"),
+ password=form.get("password"),
+ name=form.get("name"),
+ is_admin=form.get("is_admin"),
+ )
+ t = Token(user=u)
+ db.session.add(u)
+ db.session.add(t)
+
+ db.session.commit()
+
+ return http_call({"userId": u.userId, "token": t.string}, 201)
+
+ abort(400)
+
+
+@api.route("/api/users")
+@check_token
+@admin_required
+def all_users():
+ return http_call(
+ [
+ model_serialize(i, params="userId,email,is_admin,name,created_at")
+ for i in User.query.all()
+ ],
+ 200,
+ )
+
+
+@api.route("/api/user/<int:userId>")
+@check_token
+def get_user(userId):
+ return http_call(
+ model_serialize(
+ User.query.filter_by(userId=userId).first(),
+ params="userId,email,is_admin,name,created_at",
+ ),
+ 200,
+ )
+
+
+@api.route("/api/user/<userId>", methods=["DELETE"])
+@check_token
+def delete_user(userId):
+ u = User.query.filter_by(userId=userId)
+ if not u:
+ abort(404)
+
+ deleted = u.delete()
+ db.session.commit()
+
+ return http_call({"delete": deleted}, 200)
+
+
+@api.route("/api/user/<userId>", methods=["PUT"])
+@check_token
+def edit_user(userId):
+ if not request.json:
+ abort(400)
+
+ form = UserForm(request.json)
+ u = User.query.filter_by(userId=userId).first()
+ if not u:
+ abort(400)
+
+ if form.get("password"):
+ psw = True
+ else:
+ psw = False
+
+ if not psw or not form.get("is_admin") or form.is_valid():
+ u.name = form.get("name")
+ u.email = form.get("email")
+ u.is_admin = form.get("is_admin")
+ if psw:
+ crypt_psw = sha256(form.get("password").encode()).hexdigest()
+ u.password = crypt_psw
+
+ db.session.commit()
+
+ return http_call({"userId": u.userId}, 200)
+
+ abort(400)
diff --git a/frest/database.py b/frest/database.py
new file mode 100644
index 0000000..176cd52
--- /dev/null
+++ b/frest/database.py
@@ -0,0 +1,4 @@
+from flask_sqlalchemy import SQLAlchemy
+
+db = SQLAlchemy()
+config = {"DATABASE_URI": "sqlite:///database.db"}
diff --git a/frest/decorators.py b/frest/decorators.py
new file mode 100644
index 0000000..7ce79d7
--- /dev/null
+++ b/frest/decorators.py
@@ -0,0 +1,40 @@
+from flask import request, abort
+from auth.models import Token
+from functools import wraps
+
+
+def check_token(f):
+ @wraps(f)
+ def inner(*args, **kwargs):
+ userid = request.url.split("/")[-1]
+ headers = request.headers
+ if not headers.get("Authorization"):
+ abort(403)
+
+ auth = request.headers.get("Authorization")
+ token = Token.query.filter_by(string=auth).first()
+ if not token:
+ abort(403)
+
+ if userid.isdigit():
+ if int(userid) != token.user.userId and not token.user.is_admin:
+ abort(403)
+
+ return f(*args, **kwargs)
+
+ return inner
+
+
+def admin_required(f):
+ @wraps(f)
+ def inner(*args, **kwargs):
+ header = request.headers
+
+ auth = request.headers.get("Authorization")
+ token = Token.query.filter_by(string=auth).first()
+ if not token.user.is_admin:
+ abort(403)
+
+ return f(*args, **kwargs)
+
+ return inner
diff --git a/frest/forms.py b/frest/forms.py
new file mode 100644
index 0000000..8ce2e95
--- /dev/null
+++ b/frest/forms.py
@@ -0,0 +1,20 @@
+class ModelForm(object):
+ def __init__(self, model):
+ self.data = {}
+ self.model = model
+ no_params = ["metadata", "query", "query_class"]
+ self.attributes = [
+ i for i in dir(self.model) if not i.startswith("_") and i not in no_params
+ ]
+ self.ignore = []
+
+ def is_valid(self):
+ for key, value in self.data.items():
+ if key in self.attributes:
+ if (value == "" or not value) and key not in self.ignore:
+ return False
+
+ return True
+
+ def get(self, attr):
+ return self.data.get(attr)
diff --git a/frest/mail.py b/frest/mail.py
new file mode 100644
index 0000000..e553525
--- /dev/null
+++ b/frest/mail.py
@@ -0,0 +1,11 @@
+from flask_mail import Mail
+
+mail = Mail()
+config = {
+ "SERVER": "",
+ "PORT": 587,
+ "USE_TLS": True,
+ "USERNAME": "",
+ "DEFAULT_SENDER": "",
+ "PASSWORD": "",
+}
diff --git a/frest/manage/__init__.py b/frest/manage/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/frest/manage/__init__.py
diff --git a/frest/manage/bcolors.py b/frest/manage/bcolors.py
new file mode 100644
index 0000000..eb8b803
--- /dev/null
+++ b/frest/manage/bcolors.py
@@ -0,0 +1,17 @@
+class bcolors(object):
+ DARK_GREY = "\033[90m"
+ BOLD = "\033[1m"
+ ERROR = "\033[91m"
+ OK = "\033[92m"
+ WARNING = "\033[93m"
+ ENDC = "\033[0m"
+
+
+COLORS = [
+ bcolors.DARK_GREY,
+ bcolors.BOLD,
+ bcolors.ERROR,
+ bcolors.OK,
+ bcolors.WARNING,
+ bcolors.ENDC,
+]
diff --git a/frest/manage/utils.py b/frest/manage/utils.py
new file mode 100644
index 0000000..6634e05
--- /dev/null
+++ b/frest/manage/utils.py
@@ -0,0 +1,228 @@
+import os
+from .bcolors import COLORS
+import re
+
+
+ENDC = len(COLORS) - 1
+TEMPLATE_PATH = os.path.join(frest.__path__[0], "templates")
+
+
+def logging(text, _type=ENDC, end=""):
+ print(f"{COLORS[_type]}{text}{COLORS[ENDC]}", end=end)
+
+
+def logging_arg(text, *args):
+ args = [f"{COLORS[0]}{i}{COLORS[ENDC]}" for i in args]
+ print(text.format(*args), end="")
+
+
+def logo():
+ print(
+ """
+ __ _
+ / _| | |
+ | |_ _ __ ___ ___| |_
+ | _| '__/ _ \/ __| __|
+ | | | | | __/\__ \ |_
+ |_| |_| \___||___/\__|
+ \n\n"""
+ )
+
+
+def inputsr():
+ return input().strip().replace(" ", "_").lower()
+
+
+def create_field(fields):
+ field = {"name": "", "type": "", "nullable": True}
+ logging("Choose field name: ")
+ field_name = inputsr()
+ while (len(field_name) < 1 or field_name[0].isdigit()) or (
+ field_name in fields or field_name in ["id", "created_at", "updated_at"]
+ ):
+ if len(field_name) < 1 or field_name[0].isdigit():
+ logging("Field name must not be empty or starts with a number", 2, "\n")
+ else:
+ logging("Field name already exists", 2, "\n")
+
+ logging("Choose field name: ")
+ field_name = inputsr()
+
+ field["name"] = field_name
+
+ logging("Choose field type: ")
+ logging("int, str, text, datetime, float, bool", 0, " ")
+ field_type = inputsr()
+ if field_type not in ["int", "str", "text", "datetime", "float", "bool"]:
+ logging("Field type must be one of the supported type", 2, "\n")
+ logging("Choose field type: ")
+ logging("int, str, text, datetime, float, bool", 0, " ")
+ field_type = inputsr()
+
+ if field_type == "str":
+ logging("Choose string size: ")
+ stringsize = inputsr()
+ while not stringsize.isdigit():
+ logging("String size must be an integer number", 2, "\n")
+ logging("Choose string size: ")
+ stringsize = inputsr()
+
+ stringsize = int(stringsize)
+ if stringsize < 1:
+ logging("You inserted 0, so it will be 1 by default", 4, "\n")
+ stringsize = 1
+
+ field["size"] = stringsize
+
+ field["type"] = field_type
+
+ logging("Field is nullable? (Y/n): ")
+ field_nullable = inputsr()
+
+ if len(field_nullable) > 0:
+ while True:
+ if field_nullable[0] not in ["y", "n"]:
+ logging("Field is nullable? (Y/n): ")
+ field_nullable = inputsr()
+ else:
+ break
+
+ if field_nullable[0] == "n":
+ field["nullable"] = False
+
+ return field
+
+
+def create_model_cli(name):
+ logging(
+ "Fields id, created_at, updated_at are default on every new model, you can delete it from model file",
+ 4,
+ "\n\n",
+ )
+ fields = []
+ logging("Create field? (Y/n): ")
+ answer = inputsr()
+
+ while answer in ["y", "", "yes"]:
+ field = create_field(fields)
+ fields.append(field)
+
+ logging("Create new field? (Y/n): ")
+ answer = inputsr()
+
+ fields_string = ""
+ init_fields_s = ""
+ for field in fields:
+ field_string = f"{field['name']} = db.Column("
+ if field["type"] == "int":
+ field_string += "db.Integer"
+ elif field["type"] == "str":
+ field_string += f"db.String({field['size']})"
+ elif field["type"] == "text":
+ field_string += "db.Text"
+ elif field["type"] == "datetime":
+ field_string += "db.Datetime"
+ elif field["type"] == "float":
+ field_string += "db.Float"
+ else:
+ field_string += "db.Boolean"
+
+ field_string += f", nullable={field['nullable']})\n\t".replace("\t", " " * 4)
+
+ fields_string += field_string
+ init_fields_s += f"self.{field['name']} = kwargs.get('{field['name']}')\n\t\t".replace(
+ "'", '"'
+ ).replace(
+ "\t", " " * 4
+ )
+
+ with open(os.path.join(TEMPLATE_PATH, "models.txt")) as f:
+ modeltext = "".join(f.readlines())
+
+ modeltext = modeltext.replace("%%NAME%%", name.capitalize())
+ modeltext = modeltext.replace("%%name%%", name)
+ modeltext = modeltext.replace("%%params_model%%", fields_string[:-5])
+ modeltext = modeltext.replace("%%params_model_init%%", init_fields_s[:-9])
+
+ modelpath = f"scheme/{name}/models.py"
+ logging_arg("Create {}... ", modelpath)
+ with open(modelpath, "w") as f:
+ f.write(modeltext)
+
+ return fields
+
+
+def create_forms(name):
+ with open(os.path.join(TEMPLATE_PATH, "form.txt")) as f:
+ formstext = "".join(f.readlines())
+
+ formstext = formstext.replace("%%NAME%%", name.capitalize())
+ with open(f"scheme/{name}/forms.py", "w") as f:
+ f.write(formstext)
+
+
+def create_routes(name, fields):
+ fields_l = [f"{name}Id"]
+ fields_l.extend([i["name"] for i in fields])
+ fields_l.extend(["created_at", "updated_at"])
+
+ params_form = ""
+ for i in fields:
+ params_form += f"{i['name']}=form.get('{i['name']}'),\n\t\t\t".replace(
+ "'", '"'
+ ).replace("\t", " " * 4)
+
+ params_put = ""
+ for i in fields:
+ params_put += f"{name[0]}.{i['name']} = form.get('{i['name']}')\n\t\t".replace(
+ "'", '"'
+ ).replace("\t", " " * 4)
+
+ with open(os.path.join(TEMPLATE_PATH, "routes.txt")) as f:
+ routestext = "".join(f.readlines())
+
+ routestext = routestext.replace("%%NAME%%", name.capitalize())
+ routestext = routestext.replace("%%name%%", name)
+ routestext = routestext.replace("%%params%%", ",".join(fields_l))
+ routestext = routestext.replace("%%first_char%%", name[0])
+ routestext = routestext.replace("%%params_form%%", params_form[:-13])
+ routestext = routestext.replace("%%params_put%%", params_put[:-9])
+
+ with open(f"scheme/{name}/routes.py", "w") as f:
+ f.write(routestext)
+
+
+def create_app(name):
+ name = name.lower().replace("-", "_")
+
+ if name.isdigit() or name[0].isdigit():
+ logging("Name cannot be a number o starts with a number", 2)
+ return
+
+ if len(name) < 2:
+ logging("Name of app must be minimun 2 characters long", 2)
+ return
+
+ if os.path.exists(f"scheme/{name}"):
+ logging("App already exists", 2)
+ return
+
+ logging_arg("Create {}... ", f"scheme/{name}")
+ os.mkdir(f"scheme/{name}")
+ logging("OK", 3, "\n")
+
+ logging_arg("Create {}... ", f"scheme/{name}/__init__.py")
+ open(f"scheme/{name}/__init__.py", "w").close()
+ logging("OK", 3, "\n")
+
+ logging_arg("Create model for {}...\n", name)
+ fields = create_model_cli(name)
+ logging("OK", 3, "\n")
+
+ logging_arg("Create {}... ", f"scheme/{name}/forms.py")
+ create_forms(name)
+ logging("OK", 3, "\n")
+
+ logging_arg("Create {}... ", f"scheme/{name}/routes.py")
+ create_routes(name, fields)
+ logging("OK", 3, "\n")
diff --git a/frest/templates/form.txt b/frest/templates/form.txt
new file mode 100644
index 0000000..ed1017e
--- /dev/null
+++ b/frest/templates/form.txt
@@ -0,0 +1,11 @@
+from .models import %%NAME%%
+from src.forms import ModelForm
+
+
+class %%NAME%%Form(ModelForm):
+ model = %%NAME%%
+
+ def __init__(self, data):
+ super().__init__(self.model)
+ self.data = data
+ self.ignore = []
diff --git a/frest/templates/models.txt b/frest/templates/models.txt
new file mode 100644
index 0000000..af80963
--- /dev/null
+++ b/frest/templates/models.txt
@@ -0,0 +1,26 @@
+from database import db
+from datetime import datetime
+from pytz import timezone
+import os
+
+
+class %%NAME%%(db.Model):
+ %%name%%Id = db.Column(db.Integer, primary_key=True)
+ %%params_model%%
+ created_at = db.Column(db.DateTime)
+ updated_at = db.Column(db.DateTime)
+
+ def __init__(self, **kwargs):
+ %%params_model_init%%
+ self.created_at = datetime.now(
+ timezone(os.getenv("FREST_TIMEZONE", "Europe/Rome"))
+ )
+ self.updated_at = datetime.now(
+ timezone(os.getenv("FREST_TIMEZONE", "Europe/Rome"))
+ )
+
+ def __repr__(self):
+ return f"<%%NAME%% '{self.%%name%%Id}'>"
+
+ def __str__(self):
+ return f"{self.%%name%%Id}"
diff --git a/frest/templates/routes.txt b/frest/templates/routes.txt
new file mode 100644
index 0000000..11ba63a
--- /dev/null
+++ b/frest/templates/routes.txt
@@ -0,0 +1,88 @@
+from flask import Blueprint, request, abort
+from frest.utils import http_call, model_serialize
+from frest.decorators import check_token, admin_required
+from .models import %%NAME%%
+from .forms import %%NAME%%Form
+from database import db
+import json
+from datetime import datetime
+from pytz import timezone
+
+api = Blueprint("%%name%%s", __name__)
+
+
+@api.route("/api/%%name%%")
+def all_%%name%%s():
+ return http_call(
+ [
+ model_serialize(i, params="%%params%%")
+ for i in %%NAME%%.query.all()
+ ],
+ 200,
+ )
+
+
+@api.route("/api/%%name%%/<%%name%%Id>")
+def get_%%name%%(%%name%%Id):
+ %%first_char%% = %%NAME%%.query.filter_by(%%name%%Id=%%name%%Id).first()
+ if not %%first_char%%:
+ abort(404)
+
+ return http_call(model_serialize(%%first_char%%, params="%%params%%"), 200)
+
+
+@api.route("/api/%%name%%/<%%name%%Id>", methods=["DELETE"])
+@check_token
+def delete_%%name%%(%%name%%Id):
+ %%first_char%% = %%NAME%%.query.filter_by(%%name%%Id=%%name%%Id)
+ if not %%first_char%%:
+ abort(404)
+
+ deleted = %%first_char%%.delete()
+ db.session.commit()
+
+ return http_call({"delete": deleted}, 200)
+
+
+@api.route("/api/%%name%%", methods=["POST"])
+@check_token
+def new_%%name%%():
+ if not request.json:
+ abort(400)
+
+ form = %%NAME%%Form(request.json)
+
+ if form.is_valid():
+ %%first_char%% = %%NAME%%(
+ %%params_form%%
+ )
+ db.session.add(%%first_char%%)
+
+ db.session.commit()
+
+ return http_call({"%%name%%Id": %%first_char%%.%%name%%Id}, 201)
+
+ abort(400)
+
+
+@api.route("/api/%%name%%/<%%name%%Id>", methods=["PUT"])
+@check_token
+def edit_%%name%%(%%name%%Id):
+ if not request.json:
+ abort(400)
+
+ form = %%NAME%%Form(request.json)
+
+ if form.is_valid():
+ %%first_char%% = %%NAME%%.query.filter_by(%%name%%Id=%%name%%Id).first()
+ if not %%first_char%%:
+ abort(404)
+
+ %%params_put%%
+ %%first_char%%.updated_at = datetime.now(timezone("Europe/Rome"))
+
+ db.session.commit()
+
+ return http_call({"%%name%%Id": %%first_char%%.%%name%%Id}, 200)
+
+ abort(400)
diff --git a/frest/utils.py b/frest/utils.py
new file mode 100644
index 0000000..ef673a7
--- /dev/null
+++ b/frest/utils.py
@@ -0,0 +1,44 @@
+from flask import make_response, jsonify, request, render_template
+from flask_mail import Message
+from frest.mail import mail
+from datetime import datetime
+import os
+
+
+def send_email(sender, email, activation_code, title, template):
+ msg = Message(title, sender=sender, recipients=[email])
+ rest_link = os.getenv("FREST_URL", "http://localhost:8080/app")
+ msg.html = render_template(template, link=rest_link, code=activation_code)
+ mail.send(msg)
+
+
+def http_call(data, status):
+ return make_response(jsonify({"status": status, "result": data}), status)
+
+
+def model_serialize(obj, params="", extend_model_for=[]):
+ fields = {}
+ params = params.split(",")
+
+ for i in [f for f in dir(obj) if f in params]:
+ if isinstance(obj.__getattribute__(i), datetime):
+ fields[i] = obj.__getattribute__(i).strftime("%d/%m/%Y %H:%M")
+ else:
+ fields[i] = obj.__getattribute__(i)
+
+ if len(extend_model_for) > 0:
+ for key, value in fields.items():
+ if isinstance(value, list):
+ _l = []
+ for v in value:
+ for i in extend_model_for:
+ if isinstance(v, i):
+ _l.append(v.as_json())
+
+ fields[key] = _l
+ else:
+ for i in extend_model_for:
+ if isinstance(value, i):
+ fields[key] = value.as_json()
+
+ return fields
diff --git a/frest/wsgi.py b/frest/wsgi.py
new file mode 100644
index 0000000..6026b0f
--- /dev/null
+++ b/frest/wsgi.py
@@ -0,0 +1,4 @@
+from app import app
+
+if __name__ == "__main__":
+ app.run()