Initial commit
This commit is contained in:
parent
b8bdc46525
commit
4cd9db36cd
10 changed files with 206 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
.venv
|
||||||
|
*.pyc
|
28
Dockerfile
Normal file
28
Dockerfile
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
FROM python:3.6-slim
|
||||||
|
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get install -y libpq-dev python-dev gcc
|
||||||
|
|
||||||
|
## make a local directory
|
||||||
|
RUN mkdir /app
|
||||||
|
|
||||||
|
# set "app" as the working directory from which CMD, RUN, ADD references
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# copy requirements.txt to /app
|
||||||
|
ADD requirements.txt .
|
||||||
|
|
||||||
|
# required to be able to install old MarkupSafe==1.0.0 version
|
||||||
|
RUN pip install --upgrade pip setuptools==45.2.0
|
||||||
|
|
||||||
|
# pip install the local requirements.txt
|
||||||
|
RUN pip install -r requirements.txt
|
||||||
|
|
||||||
|
# now copy all the files in this directory to /code
|
||||||
|
ADD . .
|
||||||
|
|
||||||
|
# Listen to port 80 at runtime
|
||||||
|
EXPOSE 5000
|
||||||
|
|
||||||
|
# Define our command to be run when launching the container
|
||||||
|
CMD ["gunicorn", "app:app", "-b", "0.0.0.0:5000", "--workers", "4", "--reload", "--capture-output", "--enable-stdio-inheritance", "--log-level", "DEBUG"]
|
3
api/__init__.py
Normal file
3
api/__init__.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
from flask import Blueprint
|
||||||
|
|
||||||
|
api_v1 = Blueprint('api_v1', __name__, url_prefix='/api/v1')
|
66
api/apps.py
Normal file
66
api/apps.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
from flask import jsonify
|
||||||
|
from flask_jwt_extended import jwt_required
|
||||||
|
from flask_cors import cross_origin
|
||||||
|
|
||||||
|
from . import api_v1
|
||||||
|
|
||||||
|
CONFIG_DATA = [
|
||||||
|
{
|
||||||
|
"id": "values.yml",
|
||||||
|
"description": "Some user friendly description",
|
||||||
|
"raw": "cronjob:\n # Set curl to accept insecure connections when acme staging is used\n curlInsecure: false",
|
||||||
|
"fields": [
|
||||||
|
{"name": "cronjob", "type": "string", "value": ""},
|
||||||
|
{"name": "curlInsecure", "type": "boolean", "value": "false"}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
APPS_DATA = [
|
||||||
|
{"id": 1, "name": "Nextcloud", "enabled": True, "status": "ON for everyone"},
|
||||||
|
{"id": 2, "name": "Rocketchat", "enabled": True, "status": "ON for everyone"},
|
||||||
|
{"id": 3, "name": "Wordpress", "enabled": False, "status": "ON for everyone"}
|
||||||
|
]
|
||||||
|
|
||||||
|
APP_DATA = {"id": 1, "name": "Nextcloud", "selected": True, "status": "ON for everyone", "config": CONFIG_DATA},
|
||||||
|
|
||||||
|
|
||||||
|
@api_v1.route('/apps', methods=['GET'])
|
||||||
|
@jwt_required()
|
||||||
|
@cross_origin()
|
||||||
|
def get_apps():
|
||||||
|
return jsonify(APPS_DATA)
|
||||||
|
|
||||||
|
|
||||||
|
@api_v1.route('/apps/<string:slug>', methods=['GET'])
|
||||||
|
@jwt_required()
|
||||||
|
def get_app(slug):
|
||||||
|
return jsonify(APPS_DATA[0])
|
||||||
|
|
||||||
|
|
||||||
|
@api_v1.route('/apps', methods=['POST'])
|
||||||
|
@jwt_required()
|
||||||
|
@cross_origin()
|
||||||
|
def post_app():
|
||||||
|
return jsonify(APPS_DATA), 201
|
||||||
|
|
||||||
|
|
||||||
|
@api_v1.route('/apps/<string:slug>', methods=['PUT'])
|
||||||
|
@jwt_required()
|
||||||
|
@cross_origin()
|
||||||
|
def put_app(slug):
|
||||||
|
return jsonify(APPS_DATA)
|
||||||
|
|
||||||
|
|
||||||
|
@api_v1.route('/apps/<string:slug>/config', methods=['GET'])
|
||||||
|
@jwt_required()
|
||||||
|
@cross_origin()
|
||||||
|
def get_config(slug):
|
||||||
|
return jsonify(CONFIG_DATA)
|
||||||
|
|
||||||
|
|
||||||
|
@api_v1.route('/apps/<string:slug>/config', methods=['DELETE'])
|
||||||
|
@jwt_required()
|
||||||
|
@cross_origin()
|
||||||
|
def delete_config(slug):
|
||||||
|
return jsonify(CONFIG_DATA)
|
21
api/auth.py
Normal file
21
api/auth.py
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
from flask import request, jsonify
|
||||||
|
from flask_jwt_extended import create_access_token
|
||||||
|
from flask_cors import cross_origin
|
||||||
|
|
||||||
|
from . import api_v1
|
||||||
|
|
||||||
|
USERNAME = 'admin'
|
||||||
|
PASSWORD = 'admin'
|
||||||
|
|
||||||
|
|
||||||
|
@api_v1.route('/login', methods=['POST'])
|
||||||
|
@cross_origin()
|
||||||
|
def login():
|
||||||
|
username = request.json.get('username')
|
||||||
|
password = request.json.get('password')
|
||||||
|
|
||||||
|
if username != USERNAME or password != PASSWORD:
|
||||||
|
return jsonify({'errorMessage': 'Invalid username or password'}), 401
|
||||||
|
|
||||||
|
access_token = create_access_token(identity=username)
|
||||||
|
return jsonify({'username': USERNAME, 'access_token': access_token})
|
38
api/users.py
Normal file
38
api/users.py
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
from flask import jsonify
|
||||||
|
from flask_jwt_extended import jwt_required
|
||||||
|
from flask_cors import cross_origin
|
||||||
|
|
||||||
|
from . import api_v1
|
||||||
|
|
||||||
|
|
||||||
|
USER_DATA = [
|
||||||
|
{"id": 1, "email": "john@doe.com", "name": "John Doe", "status": "active", "last_login": "2021-08-03T07:40:51+00:00"}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@api_v1.route('/users', methods=['GET'])
|
||||||
|
@jwt_required()
|
||||||
|
@cross_origin()
|
||||||
|
def get_users():
|
||||||
|
return jsonify(USER_DATA)
|
||||||
|
|
||||||
|
|
||||||
|
@api_v1.route('/users', methods=['POST'])
|
||||||
|
@jwt_required()
|
||||||
|
@cross_origin()
|
||||||
|
def post_user():
|
||||||
|
return jsonify(USER_DATA), 201
|
||||||
|
|
||||||
|
|
||||||
|
@api_v1.route('/users/<int:id>', methods=['PUT'])
|
||||||
|
@jwt_required()
|
||||||
|
@cross_origin()
|
||||||
|
def put_user(id):
|
||||||
|
return jsonify(USER_DATA)
|
||||||
|
|
||||||
|
|
||||||
|
@api_v1.route('/users/<int:id>', methods=['DELETE'])
|
||||||
|
@jwt_required()
|
||||||
|
@cross_origin()
|
||||||
|
def delete_user(id):
|
||||||
|
return jsonify(USER_DATA)
|
27
app.py
Normal file
27
app.py
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
from flask import Flask, jsonify
|
||||||
|
from flask_jwt_extended import JWTManager
|
||||||
|
from flask_cors import CORS, cross_origin
|
||||||
|
|
||||||
|
from config import *
|
||||||
|
|
||||||
|
from api import api_v1, auth, users, apps
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
cors = CORS(app)
|
||||||
|
app.config['SECRET_KEY'] = SECRET_KEY
|
||||||
|
app.register_blueprint(api_v1)
|
||||||
|
|
||||||
|
jwt = JWTManager(app)
|
||||||
|
|
||||||
|
|
||||||
|
# When token is not valid or missing handler
|
||||||
|
@jwt.invalid_token_loader
|
||||||
|
@jwt.unauthorized_loader
|
||||||
|
@jwt.expired_token_loader
|
||||||
|
def expired_token_callback(*args):
|
||||||
|
return jsonify({'errorMessage': 'Unauthorized'}), 401
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
return 'Open App Stack API v1.0'
|
3
config.py
Normal file
3
config.py
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
import os
|
||||||
|
|
||||||
|
SECRET_KEY = os.environ.get('SECRET_KEY')
|
14
requirements.txt
Normal file
14
requirements.txt
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
cffi==1.14.6
|
||||||
|
click==8.0.1
|
||||||
|
cryptography==3.4.7
|
||||||
|
Flask==2.0.1
|
||||||
|
Flask-Cors==3.0.10
|
||||||
|
Flask-JWT-Extended==4.2.3
|
||||||
|
gunicorn==20.1.0
|
||||||
|
itsdangerous==2.0.1
|
||||||
|
Jinja2==3.0.1
|
||||||
|
MarkupSafe==2.0.1
|
||||||
|
pycparser==2.20
|
||||||
|
PyJWT==2.1.0
|
||||||
|
six==1.16.0
|
||||||
|
Werkzeug==2.0.1
|
4
run_app.sh
Executable file
4
run_app.sh
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
export FLASK_APP=app.py
|
||||||
|
export FLASK_ENV=development
|
||||||
|
export SECRET_KEY="e38hq!@0n64g@qe6)5csk41t=ljo2vllog(%k7njnm4b@kh42c"
|
||||||
|
flask run
|
Loading…
Reference in a new issue