initial
This commit is contained in:
commit
9202a2a80e
13 changed files with 1348 additions and 0 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
__pycache__
|
||||||
|
.direnv
|
||||||
|
test.db
|
3
.style.yapf
Normal file
3
.style.yapf
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[style]
|
||||||
|
based_on_style = pep8
|
||||||
|
indent_width = 2
|
0
README.md
Normal file
0
README.md
Normal file
10
default.nix
Normal file
10
default.nix
Normal 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
40
flake.lock
Normal 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
20
flake.nix
Normal 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
0
minitrix/__init__.py
Normal file
63
minitrix/app.py
Normal file
63
minitrix/app.py
Normal 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
17
minitrix/db.py
Normal 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
48
minitrix/views/r0.py
Normal 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
1120
poetry.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
23
pyproject.toml
Normal file
23
pyproject.toml
Normal 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"
|
Loading…
Reference in a new issue