chore: first commit

This commit is contained in:
ka 2024-06-15 02:34:45 -03:00
commit d908d38e75
Signed by: ka
GPG Key ID: 77D32BB1496F3FD1
14 changed files with 555 additions and 0 deletions

15
.env.example Normal file
View File

@ -0,0 +1,15 @@
OAUTH_CLIENT_ID = ""
OAUTH_CLIENT_SECRET = ""
OAUTH_REDIRECT_URI = "http://localhost:8080/callback"
OAUTH_URL_AUTHORIZE = "https://www.bling.com.br/Api/v3/oauth/authorize"
OAUTH_URL_ACCESS_TOKEN = "https://www.bling.com.br/Api/v3/oauth/token"
# DATABASE
DB_NAME = 'ajusta_bling'
DB_USER = 'ajusta_bling'
DB_PASS = 'ajusta_bling'
DB_HOST = 'winhost'
DB_PORT = '5432'
WEB_HOST = "0.0.0.0"
WEB_PORT = "8080"

163
.gitignore vendored Normal file
View File

@ -0,0 +1,163 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# Vscode
.vscode/

0
ajusta_bling/__init__.py Normal file
View File

125
ajusta_bling/__main__.py Normal file
View File

@ -0,0 +1,125 @@
import argparse
import os
from typing import Sequence
from dotenv import load_dotenv
"""
This is always the entry point so you don't technically need to do the
__name__ == "__main__" check, but its still common practice to show that
this is the entry point.
You would start your project/app by first installing it.
You can install it as an editable package during development as this symlinks it instead.
Example (in venv):
pip install -e .
python -m ajusta_bling args...
"""
class EnvDefault(argparse.Action):
def __init__(self, envvar, required=False, default=None, **kwargs):
if not default and envvar:
if envvar in os.environ:
default = os.environ[envvar]
if required and default:
required = False
super(EnvDefault, self).__init__(default=default, required=required,
**kwargs)
def __call__(self, parser, namespace, values, option_string=None):
setattr(namespace, self.dest, values)
def main(argv: Sequence[str] | None = None) -> int:
"""
This is where I would put argparse (if you want to use this as a callable package with args)
If not then I use this as setup.
"""
parser = argparse.ArgumentParser(
prog=__package__ or "ajusta_bling",
)
parser.add_argument(
"--dbuser", "-du",
type=str,
action=EnvDefault,
required=True,
envvar="DB_USER",
help="PostgreSQL User",
)
parser.add_argument(
"--dbpassword", "-dP",
type=str,
action=EnvDefault,
envvar="DB_PASS",
help="PostgreSQL Password",
)
parser.add_argument(
"--dbhost", "-dh",
type=str,
action=EnvDefault,
required=True,
envvar="DB_HOST",
help="PostgreSQL Host",
)
parser.add_argument(
"--dbport", "-dp",
type=int,
action=EnvDefault,
required=True,
envvar="DB_PORT",
help="PostgreSQL Port",
)
parser.add_argument(
"--dbname", "-db",
type=str,
action=EnvDefault,
required=True,
envvar="DB_NAME",
help="PostgreSQL Database",
)
parser.add_argument(
"--webhost", "-wh",
type=str,
action=EnvDefault,
required=True,
envvar="WEB_HOST",
help="Web server host",
)
parser.add_argument(
"--webport", "-wp",
type=int,
action=EnvDefault,
required=True,
envvar="WEB_PORT",
help="Web server port",
)
parser.add_argument(
"--debug",
type=bool,
action=EnvDefault,
envvar="WEB_DEBUG",
default=False,
help="Enable web server debug",
)
args = parser.parse_args(argv)
from . import app
from .app import Args
args = Args(**vars(args))
app.main(args)
return 0
if __name__ == "__main__":
load_dotenv(".env")
raise SystemExit(main())

10
ajusta_bling/app.py Normal file
View File

@ -0,0 +1,10 @@
import database
import web
from ajusta_bling.common import Args
from ajusta_bling.database import Database
def main(args: Args):
db = Database(args)
web.run(args, db)

15
ajusta_bling/common.py Normal file
View File

@ -0,0 +1,15 @@
from dataclasses import dataclass
@dataclass
class Args:
dbname: str
dbuser: str
dbpassword: str
dbhost: str
dbport: int
webhost: str
webport: int
debug: bool = True

View File

@ -0,0 +1,43 @@
from __future__ import annotations
from contextlib import contextmanager
from time import sleep
from typing import Generator
import psycopg2
import psycopg2.extensions
import psycopg2.pool
from ajusta_bling.common import Args
class Database:
pool: psycopg2.pool.SimpleConnectionPool | None = None
def __init__(self, args: Args):
self.user = args.dbuser
self.pswd = args.dbpassword
self.host = args.dbhost
self.port = args.dbport
self.db = args.dbname
def connect(self):
self.pool = psycopg2.pool.SimpleConnectionPool(
1, 10,
user = self.user,
password = self.pswd,
host = self.host,
port = self.port,
database = self.db,
)
@contextmanager
def get_cur(self) -> Generator[psycopg2.extensions.cursor]:
if self.pool is None:
self.connect()
conn = self.pool.getconn()
try:
yield conn.cursor()
conn.commit()
finally:
self.pool.putconn(conn)

0
ajusta_bling/py.typed Normal file
View File

View File

@ -0,0 +1,91 @@
from __future__ import annotations
import secrets
from base64 import b64encode
from os import getenv
import bcrypt
import requests
from flask import Flask, redirect, render_template, request, session, url_for
from ajusta_bling.common import Args
from ajusta_bling.database import Database
app = Flask(__name__)
app.secret_key = "#^A549639t5@#&$p"
db: Database | None = None
@app.route('/auth')
def auth():
session["state"] = secrets.token_urlsafe(16)
return redirect("%(url)s?client_id=%(client_id)s&redirect_uri=%(redirect)s&response_type=code&state=%(state)s" % {
"url": getenv('OAUTH_URL_AUTHORIZE'),
"client_id": getenv('OAUTH_CLIENT_ID'),
"redirect": url_for('callback', _external = True),
"state": session["state"]
})
@app.route('/callback', methods = ["GET"])
def callback():
if request.method != "GET":
return "I curse you!", 403
if request.args.get("state") != session.pop("state", "fartnugget"):
return "I banish thee, to the state of Ohio", 403
payload = {
'grant_type': 'authorization_code',
'code': request.args.get('code'),
}
header = "Basic " + b64encode(f"{getenv('OAUTH_CLIENT_ID')}:{getenv('OAUTH_CLIENT_SECRET')}".encode()).decode()
response = requests.post(getenv('OAUTH_URL_ACCESS_TOKEN'),
data = payload,
headers= {"Authorization": header})
data = response.json()
print(response.url)
print(data)
access_token: str = str(data["access_token"])
refresh_token: str = str(data["refresh_token"])
expires_in: int = int(data["expires_in"])
with db.get_cur() as cur: # TODO
cur.execute(
"""
INSERT INTO auth.tokens
(
ip_address,
access_token,
refresh_token,
expires_in
) VALUES (
%s,
%s,
%s,
%s
)
""",
(
request.remote_addr, # ip
access_token,
refresh_token,
str(expires_in),
)
)
#TODO: Store the session in the database
#insert_session(payload['access_token'], payload['refresh_token'], payload['expires_in'])
return redirect(url_for('index'))
@app.route("/")
def index():
return "YIPEE"
def run(args: Args, _db: Database):
global db
db = _db
app.run(host = args.webhost, port = args.webport,
threaded = True, use_reloader = True, debug = True)

View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>bap</title>
</head>
<body>
<h1>bap</h1>
</body>
</html>

23
pyproject.toml Normal file
View File

@ -0,0 +1,23 @@
[build-system]
requires = ["setuptools>=42.0", "wheel"]
build-backend = "setuptools.build_meta"
[tool.pytest.ini_options]
addopts = "--cov=ajusta_bling"
testpaths = [
"tests",
]
[tool.mypy]
mypy_path = "ajusta_bling"
check_untyped_defs = true
disallow_any_generics = true
ignore_missing_imports = true
no_implicit_optional = true
show_error_codes = true
strict_equality = true
warn_redundant_casts = true
warn_return_any = true
warn_unreachable = true
warn_unused_configs = true
no_implicit_reexport = true

15
requirements.txt Normal file
View File

@ -0,0 +1,15 @@
blinker==1.8.2
certifi==2024.6.2
charset-normalizer==3.3.2
click==8.1.7
Flask==3.0.3
idna==3.7
itsdangerous==2.2.0
Jinja2==3.1.4
MarkupSafe==2.1.5
psycopg2-binary==2.9.9
python-dotenv==1.0.1
requests==2.32.3
urllib3==2.2.1
Werkzeug==3.0.3
bcrypt==4.1.3

40
setup.cfg Normal file
View File

@ -0,0 +1,40 @@
[metadata]
name = ajusta_bling
description = Ajust the bling
version = 0.0.1
author = Djonathan de Souza
license = GNU
license_files = LICENSE
platforms = unix, linux, osx, cygwin, win32
classifiers =
Programming Language :: Python :: 3
Programming Language :: Python :: 3 :: Only
Programming Language :: Python :: 3.8
Programming Language :: Python :: 3.9
Programming Language :: Python :: 3.10
Programming Language :: Python :: 3.11
Programming Language :: Python :: 3.12
[options]
package_dir =
=ajusta_bling
packages = find:
python_requires = >=3.6
include_package_data = true
[options.packages.find]
where = ajusta_bling
[options.extras_require]
testing =
pytest>=7.0
pytest-cov>=3.0
mypy>=0.960
flake8>=4.0
tox>=3.25
[options.package_data]
ajusta_bling = py.typed
[flake8]
max-line-length = 160

4
setup.py Normal file
View File

@ -0,0 +1,4 @@
from setuptools import setup
if __name__ == "__main__":
setup(version="4.2.0")