This commit is contained in:
Michael Zhang 2023-01-11 23:53:41 -06:00
commit 9202a2a80e
13 changed files with 1348 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
__pycache__
.direnv
test.db

3
.style.yapf Normal file
View file

@ -0,0 +1,3 @@
[style]
based_on_style = pep8
indent_width = 2

0
README.md Normal file
View file

10
default.nix Normal file
View file

@ -0,0 +1,10 @@
{ buildPythonApplication, poetry, flask }:
buildPythonApplication {
pname = "minitrix";
version = "0.1.0";
projectDir = ./.;
nativeBuildInputs = [ poetry ];
buildInputs = [ flask ];
}

40
flake.lock Normal file
View file

@ -0,0 +1,40 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1667395993,
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
"type": "github"
},
"original": {
"id": "flake-utils",
"type": "indirect"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1667629849,
"narHash": "sha256-P+v+nDOFWicM4wziFK9S/ajF2lc0N2Rg9p6Y35uMoZI=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "3bacde6273b09a21a8ccfba15586fb165078fb62",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

20
flake.nix Normal file
View file

@ -0,0 +1,20 @@
{
description = "A very basic flake";
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs { inherit system; };
flakePkgs = rec {
minitrix = pkgs.python310Packages.callPackage ./. { };
};
in rec {
packages = flake-utils.lib.flattenTree flakePkgs;
devShell = pkgs.mkShell {
inputsFrom = with packages; [ ];
packages = with pkgs.python310Packages; [ poetry yapf ];
};
});
}

0
minitrix/__init__.py Normal file
View file

63
minitrix/app.py Normal file
View file

@ -0,0 +1,63 @@
import logging
from aiohttp import web
from aiohttp.web import Request, Response, middleware
import aiohttp_sqlalchemy as ahsa
from minitrix.db import metadata
import minitrix.views.r0
logging.basicConfig(level=logging.DEBUG)
@middleware
async def cors_middleware(request: Request, handler):
if request.method == "OPTIONS": resp = Response()
else: resp = await handler(request)
resp.headers.add("Access-Control-Allow-Origin", "*")
resp.headers.add("Access-Control-Allow-Methods",
"GET, POST, PUT, DELETE, OPTIONS")
resp.headers.add(
"Access-Control-Allow-Headers",
"Origin, X-Requested-With, Content-Type, Accept, Authorization")
return resp
routes = web.RouteTableDef()
@routes.get("/.well-known/matrix/client")
async def well_known_matrix_client(_):
return web.json_response({
"m.homeserver": {
"base_url": "https://0a7c-207-153-38-50.ngrok.io"
},
"m.identity_server": {
"base_url": "https://identity.vector.im"
},
})
@routes.get("/_matrix/client/versions")
async def matrix_client_versions(_):
# https://matrix.org/docs/spec/client_server/r0.6.1.html
return web.json_response({"unstable_features": {}, "versions": ["r0.6.1"]})
async def app_factory():
app = web.Application(middlewares=[cors_middleware])
ahsa.setup(app, [ahsa.bind("sqlite+aiosqlite:///test.db")])
await ahsa.init_db(app, metadata)
app.add_routes(routes)
app.add_routes(minitrix.views.r0.routes)
return app
if __name__ == '__main__':
app = app_factory()
web.run_app(app, port=8888)

17
minitrix/db.py Normal file
View file

@ -0,0 +1,17 @@
import uuid
import sqlalchemy as sa
from sqlalchemy import orm
metadata = sa.MetaData()
Base = orm.declarative_base(metadata=metadata)
class User(Base):
__tablename__ = "users"
id = sa.Column(sa.String,
primary_key=True,
default=lambda: str(uuid.uuid4()))
username = sa.Column(sa.String)
password = sa.Column(sa.String)

48
minitrix/views/r0.py Normal file
View file

@ -0,0 +1,48 @@
from aiohttp import web
from aiohttp.web_exceptions import HTTPBadRequest, HTTPForbidden
from passlib.hash import bcrypt_sha256
import aiohttp_sqlalchemy as ahsa
from sqlalchemy import select
from minitrix.db import User
DOMAIN = "0a7c-207-153-38-50.ngrok.io"
routes = web.RouteTableDef()
@routes.get("/_matrix/client/r0/login")
async def login_get(_):
return web.json_response({"flows": [{"type": "m.login.password"}]})
@routes.post("/_matrix/client/r0/login")
async def login_post(request):
db = ahsa.get_session(request)
body = await request.json()
login_type = body.get("type")
if login_type != "m.login.password": raise HTTPBadRequest()
username = body.get("identifier").get("user")
# TODO: Validate username
password = body.get("password")
async with db.begin():
res = await db.execute(select(User).filter_by(username=username))
user = res.scalars().first()
if user is None:
password_hash = bcrypt_sha256.hash(password)
user = User(username=username, password=password_hash)
db.add(user)
await db.commit()
elif not bcrypt_sha256.verify(password, user.password):
raise HTTPForbidden()
return web.json_response({
"user_id": f"@{username}:{DOMAIN}",
"access_token": "",
"device_id": "",
})

1120
poetry.lock generated Normal file

File diff suppressed because it is too large Load diff

23
pyproject.toml Normal file
View file

@ -0,0 +1,23 @@
[tool.poetry]
name = "minitrix"
version = "0.1.0"
description = ""
authors = ["Michael Zhang <mail@mzhang.io>"]
license = "MIT"
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.10"
aiohttp-sqlalchemy = "^0.34.0"
aiohttp = {extras = ["speedups"], version = "^3.8.3"}
aiosqlite = "^0.18.0"
sqlalchemy = {extras = ["asyncio"], version = "^1.4.46"}
passlib = "^1.7.4"
[tool.poetry.group.dev.dependencies]
aiohttp-devtools = "^1.0.post0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"