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