materia-server: fix directory no parent
This commit is contained in:
parent
ec41110e0b
commit
b89e8f3393
449
flake.nix
449
flake.nix
@ -1,227 +1,258 @@
|
||||
{
|
||||
description = "Materia";
|
||||
description = "Materia";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
dream2nix = {
|
||||
url = "github:nix-community/dream2nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
bonfire.url = "github:L-Nafaryus/bonfire";
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
dream2nix = {
|
||||
url = "github:nix-community/dream2nix";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
bonfire.url = "github:L-Nafaryus/bonfire";
|
||||
};
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
dream2nix,
|
||||
bonfire,
|
||||
...
|
||||
}: let
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs {inherit system;};
|
||||
bonpkgs = bonfire.packages.${system};
|
||||
bonlib = bonfire.lib;
|
||||
|
||||
outputs = { self, nixpkgs, dream2nix, bonfire, ... }:
|
||||
let
|
||||
system = "x86_64-linux";
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
bonpkgs = bonfire.packages.${system};
|
||||
bonlib = bonfire.lib;
|
||||
|
||||
dreamBuildPackage = { module, meta ? {}, extraModules ? [], extraArgs ? {} }: (
|
||||
nixpkgs.lib.evalModules {
|
||||
modules = [ module ] ++ extraModules;
|
||||
specialArgs = {
|
||||
inherit dream2nix;
|
||||
packageSets.nixpkgs = pkgs;
|
||||
} // extraArgs;
|
||||
dreamBuildPackage = {
|
||||
module,
|
||||
meta ? {},
|
||||
extraModules ? [],
|
||||
extraArgs ? {},
|
||||
}:
|
||||
(
|
||||
nixpkgs.lib.evalModules {
|
||||
modules = [module] ++ extraModules;
|
||||
specialArgs =
|
||||
{
|
||||
inherit dream2nix;
|
||||
packageSets.nixpkgs = pkgs;
|
||||
}
|
||||
).config.public // { inherit meta; };
|
||||
// extraArgs;
|
||||
}
|
||||
)
|
||||
.config
|
||||
.public
|
||||
// {inherit meta;};
|
||||
in {
|
||||
packages.x86_64-linux = {
|
||||
materia-frontend = dreamBuildPackage {
|
||||
module = {
|
||||
lib,
|
||||
config,
|
||||
dream2nix,
|
||||
...
|
||||
}: {
|
||||
name = "materia-frontend";
|
||||
version = "0.0.1";
|
||||
|
||||
in
|
||||
{
|
||||
packages.x86_64-linux = {
|
||||
materia-frontend = dreamBuildPackage {
|
||||
module = { lib, config, dream2nix, ... }: {
|
||||
name = "materia-frontend";
|
||||
version = "0.0.1";
|
||||
imports = [
|
||||
dream2nix.modules.dream2nix.WIP-nodejs-builder-v3
|
||||
];
|
||||
|
||||
imports = [
|
||||
dream2nix.modules.dream2nix.WIP-nodejs-builder-v3
|
||||
];
|
||||
mkDerivation = {
|
||||
src = ./materia-web-client/src/materia-frontend;
|
||||
};
|
||||
|
||||
mkDerivation = {
|
||||
src = ./materia-web-client/src/materia-frontend;
|
||||
};
|
||||
deps = {nixpkgs, ...}: {
|
||||
inherit
|
||||
(nixpkgs)
|
||||
fetchFromGitHub
|
||||
stdenv
|
||||
;
|
||||
};
|
||||
|
||||
deps = {nixpkgs, ...}: {
|
||||
inherit
|
||||
(nixpkgs)
|
||||
fetchFromGitHub
|
||||
stdenv
|
||||
;
|
||||
};
|
||||
WIP-nodejs-builder-v3 = {
|
||||
packageLockFile = "${config.mkDerivation.src}/package-lock.json";
|
||||
};
|
||||
};
|
||||
meta = with nixpkgs.lib; {
|
||||
description = "Materia frontend";
|
||||
license = licenses.mit;
|
||||
maintainers = with bonlib.maintainers; [L-Nafaryus];
|
||||
broken = false;
|
||||
};
|
||||
};
|
||||
|
||||
WIP-nodejs-builder-v3 = {
|
||||
packageLockFile = "${config.mkDerivation.src}/package-lock.json";
|
||||
};
|
||||
};
|
||||
meta = with nixpkgs.lib; {
|
||||
description = "Materia frontend";
|
||||
license = licenses.mit;
|
||||
maintainers = with bonlib.maintainers; [ L-Nafaryus ];
|
||||
broken = false;
|
||||
};
|
||||
};
|
||||
|
||||
materia-web-client = dreamBuildPackage {
|
||||
extraArgs = {
|
||||
inherit (self.packages.x86_64-linux) materia-frontend;
|
||||
};
|
||||
module = {config, lib, dream2nix, materia-frontend, ...}: {
|
||||
imports = [ dream2nix.modules.dream2nix.WIP-python-pdm ];
|
||||
|
||||
pdm.lockfile = ./materia-web-client/pdm.lock;
|
||||
pdm.pyproject = ./materia-web-client/pyproject.toml;
|
||||
|
||||
deps = _ : {
|
||||
python = pkgs.python3;
|
||||
};
|
||||
|
||||
mkDerivation = {
|
||||
src = ./materia-web-client;
|
||||
buildInputs = [
|
||||
pkgs.python3.pkgs.pdm-backend
|
||||
];
|
||||
configurePhase = ''
|
||||
cp -rv ${materia-frontend}/dist ./src/materia-frontend/
|
||||
'';
|
||||
};
|
||||
};
|
||||
meta = with nixpkgs.lib; {
|
||||
description = "Materia web client";
|
||||
license = licenses.mit;
|
||||
maintainers = with bonlib.maintainers; [ L-Nafaryus ];
|
||||
broken = false;
|
||||
};
|
||||
};
|
||||
|
||||
materia-server = dreamBuildPackage {
|
||||
module = {config, lib, dream2nix, materia-frontend, ...}: {
|
||||
imports = [ dream2nix.modules.dream2nix.WIP-python-pdm ];
|
||||
|
||||
pdm.lockfile = ./materia-server/pdm.lock;
|
||||
pdm.pyproject = ./materia-server/pyproject.toml;
|
||||
|
||||
deps = _ : {
|
||||
python = pkgs.python3;
|
||||
};
|
||||
|
||||
mkDerivation = {
|
||||
src = ./materia-server;
|
||||
buildInputs = [
|
||||
pkgs.python3.pkgs.pdm-backend
|
||||
];
|
||||
nativeBuildInputs = [
|
||||
pkgs.python3.pkgs.wrapPython
|
||||
];
|
||||
|
||||
};
|
||||
};
|
||||
meta = with nixpkgs.lib; {
|
||||
description = "Materia";
|
||||
license = licenses.mit;
|
||||
maintainers = with bonlib.maintainers; [ L-Nafaryus ];
|
||||
broken = false;
|
||||
mainProgram = "materia-server";
|
||||
};
|
||||
};
|
||||
|
||||
postgresql = let
|
||||
user = "postgres";
|
||||
database = "postgres";
|
||||
dataDir = "/var/lib/postgresql";
|
||||
entryPoint = pkgs.writeTextDir "entrypoint.sh" ''
|
||||
initdb -U ${user}
|
||||
postgres -k ${dataDir}
|
||||
'';
|
||||
in pkgs.dockerTools.buildImage {
|
||||
name = "postgresql";
|
||||
tag = "devel";
|
||||
|
||||
copyToRoot = pkgs.buildEnv {
|
||||
name = "image-root";
|
||||
pathsToLink = [ "/bin" "/etc" "/" ];
|
||||
paths = with pkgs; [
|
||||
bash
|
||||
postgresql
|
||||
entryPoint
|
||||
];
|
||||
};
|
||||
runAsRoot = with pkgs; ''
|
||||
#!${runtimeShell}
|
||||
${dockerTools.shadowSetup}
|
||||
groupadd -r ${user}
|
||||
useradd -r -g ${user} --home-dir=${dataDir} ${user}
|
||||
mkdir -p ${dataDir}
|
||||
chown -R ${user}:${user} ${dataDir}
|
||||
'';
|
||||
|
||||
config = {
|
||||
Entrypoint = [ "bash" "/entrypoint.sh" ];
|
||||
StopSignal = "SIGINT";
|
||||
User = "${user}:${user}";
|
||||
Env = [ "PGDATA=${dataDir}" ];
|
||||
WorkingDir = dataDir;
|
||||
ExposedPorts = {
|
||||
"5432/tcp" = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
redis = let
|
||||
user = "redis";
|
||||
dataDir = "/var/lib/redis";
|
||||
entryPoint = pkgs.writeTextDir "entrypoint.sh" ''
|
||||
redis-server \
|
||||
--daemonize no \
|
||||
--dir "${dataDir}"
|
||||
'';
|
||||
in pkgs.dockerTools.buildImage {
|
||||
name = "redis";
|
||||
tag = "devel";
|
||||
|
||||
copyToRoot = pkgs.buildEnv {
|
||||
name = "image-root";
|
||||
pathsToLink = [ "/bin" "/etc" "/" ];
|
||||
paths = with pkgs; [
|
||||
bash
|
||||
redis
|
||||
entryPoint
|
||||
];
|
||||
};
|
||||
runAsRoot = with pkgs; ''
|
||||
#!${runtimeShell}
|
||||
${dockerTools.shadowSetup}
|
||||
groupadd -r ${user}
|
||||
useradd -r -g ${user} --home-dir=${dataDir} ${user}
|
||||
mkdir -p ${dataDir}
|
||||
chown -R ${user}:${user} ${dataDir}
|
||||
'';
|
||||
|
||||
config = {
|
||||
Entrypoint = [ "bash" "/entrypoint.sh" ];
|
||||
StopSignal = "SIGINT";
|
||||
User = "${user}:${user}";
|
||||
WorkingDir = dataDir;
|
||||
ExposedPorts = {
|
||||
"6379/tcp" = {};
|
||||
};
|
||||
};
|
||||
materia-web-client = dreamBuildPackage {
|
||||
extraArgs = {
|
||||
inherit (self.packages.x86_64-linux) materia-frontend;
|
||||
};
|
||||
module = {
|
||||
config,
|
||||
lib,
|
||||
dream2nix,
|
||||
materia-frontend,
|
||||
...
|
||||
}: {
|
||||
imports = [dream2nix.modules.dream2nix.WIP-python-pdm];
|
||||
|
||||
pdm.lockfile = ./materia-web-client/pdm.lock;
|
||||
pdm.pyproject = ./materia-web-client/pyproject.toml;
|
||||
|
||||
deps = _: {
|
||||
python = pkgs.python3;
|
||||
};
|
||||
|
||||
mkDerivation = {
|
||||
src = ./materia-web-client;
|
||||
buildInputs = [
|
||||
pkgs.python3.pkgs.pdm-backend
|
||||
];
|
||||
configurePhase = ''
|
||||
cp -rv ${materia-frontend}/dist ./src/materia-frontend/
|
||||
'';
|
||||
};
|
||||
};
|
||||
meta = with nixpkgs.lib; {
|
||||
description = "Materia web client";
|
||||
license = licenses.mit;
|
||||
maintainers = with bonlib.maintainers; [L-Nafaryus];
|
||||
broken = false;
|
||||
};
|
||||
};
|
||||
|
||||
materia-server = dreamBuildPackage {
|
||||
module = {
|
||||
config,
|
||||
lib,
|
||||
dream2nix,
|
||||
materia-frontend,
|
||||
...
|
||||
}: {
|
||||
imports = [dream2nix.modules.dream2nix.WIP-python-pdm];
|
||||
|
||||
pdm.lockfile = ./materia-server/pdm.lock;
|
||||
pdm.pyproject = ./materia-server/pyproject.toml;
|
||||
|
||||
deps = _: {
|
||||
python = pkgs.python3;
|
||||
};
|
||||
|
||||
mkDerivation = {
|
||||
src = ./materia-server;
|
||||
buildInputs = [
|
||||
pkgs.python3.pkgs.pdm-backend
|
||||
];
|
||||
nativeBuildInputs = [
|
||||
pkgs.python3.pkgs.wrapPython
|
||||
];
|
||||
};
|
||||
};
|
||||
meta = with nixpkgs.lib; {
|
||||
description = "Materia";
|
||||
license = licenses.mit;
|
||||
maintainers = with bonlib.maintainers; [L-Nafaryus];
|
||||
broken = false;
|
||||
mainProgram = "materia-server";
|
||||
};
|
||||
};
|
||||
|
||||
postgresql = let
|
||||
user = "postgres";
|
||||
database = "postgres";
|
||||
dataDir = "/var/lib/postgresql";
|
||||
entryPoint = pkgs.writeTextDir "entrypoint.sh" ''
|
||||
initdb -U ${user}
|
||||
postgres -k ${dataDir}
|
||||
'';
|
||||
in
|
||||
pkgs.dockerTools.buildImage {
|
||||
name = "postgresql";
|
||||
tag = "devel";
|
||||
|
||||
copyToRoot = pkgs.buildEnv {
|
||||
name = "image-root";
|
||||
pathsToLink = ["/bin" "/etc" "/"];
|
||||
paths = with pkgs; [
|
||||
bash
|
||||
postgresql
|
||||
entryPoint
|
||||
];
|
||||
};
|
||||
runAsRoot = with pkgs; ''
|
||||
#!${runtimeShell}
|
||||
${dockerTools.shadowSetup}
|
||||
groupadd -r ${user}
|
||||
useradd -r -g ${user} --home-dir=${dataDir} ${user}
|
||||
mkdir -p ${dataDir}
|
||||
chown -R ${user}:${user} ${dataDir}
|
||||
'';
|
||||
|
||||
config = {
|
||||
Entrypoint = ["bash" "/entrypoint.sh"];
|
||||
StopSignal = "SIGINT";
|
||||
User = "${user}:${user}";
|
||||
Env = ["PGDATA=${dataDir}"];
|
||||
WorkingDir = dataDir;
|
||||
ExposedPorts = {
|
||||
"5432/tcp" = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
apps.x86_64-linux = {
|
||||
materia-server = {
|
||||
type = "app";
|
||||
program = "${self.packages.x86_64-linux.materia-server}/bin/materia-server";
|
||||
};
|
||||
};
|
||||
redis = let
|
||||
user = "redis";
|
||||
dataDir = "/var/lib/redis";
|
||||
entryPoint = pkgs.writeTextDir "entrypoint.sh" ''
|
||||
redis-server \
|
||||
--daemonize no \
|
||||
--dir "${dataDir}"
|
||||
'';
|
||||
in
|
||||
pkgs.dockerTools.buildImage {
|
||||
name = "redis";
|
||||
tag = "devel";
|
||||
|
||||
devShells.x86_64-linux.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [ postgresql redis pdm nodejs ];
|
||||
# greenlet requires libstdc++
|
||||
LD_LIBRARY_PATH = nixpkgs.lib.makeLibraryPath [ pkgs.stdenv.cc.cc ];
|
||||
copyToRoot = pkgs.buildEnv {
|
||||
name = "image-root";
|
||||
pathsToLink = ["/bin" "/etc" "/"];
|
||||
paths = with pkgs; [
|
||||
bash
|
||||
redis
|
||||
entryPoint
|
||||
];
|
||||
};
|
||||
runAsRoot = with pkgs; ''
|
||||
#!${runtimeShell}
|
||||
${dockerTools.shadowSetup}
|
||||
groupadd -r ${user}
|
||||
useradd -r -g ${user} --home-dir=${dataDir} ${user}
|
||||
mkdir -p ${dataDir}
|
||||
chown -R ${user}:${user} ${dataDir}
|
||||
'';
|
||||
|
||||
config = {
|
||||
Entrypoint = ["bash" "/entrypoint.sh"];
|
||||
StopSignal = "SIGINT";
|
||||
User = "${user}:${user}";
|
||||
WorkingDir = dataDir;
|
||||
ExposedPorts = {
|
||||
"6379/tcp" = {};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
apps.x86_64-linux = {
|
||||
materia-server = {
|
||||
type = "app";
|
||||
program = "${self.packages.x86_64-linux.materia-server}/bin/materia-server";
|
||||
};
|
||||
};
|
||||
|
||||
devShells.x86_64-linux.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [postgresql redis pdm nodejs];
|
||||
# greenlet requires libstdc++
|
||||
LD_LIBRARY_PATH = nixpkgs.lib.makeLibraryPath [pkgs.stdenv.cc.cc];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -1,11 +1,26 @@
|
||||
from materia_server.models.auth import (
|
||||
LoginType,
|
||||
LoginSource,
|
||||
OAuth2Application,
|
||||
OAuth2Grant,
|
||||
OAuth2AuthorizationCode,
|
||||
)
|
||||
|
||||
from materia_server.models.auth import LoginType, LoginSource, OAuth2Application, OAuth2Grant, OAuth2AuthorizationCode
|
||||
|
||||
from materia_server.models.database import Database, DatabaseError, DatabaseMigrationError, Cache, CacheError
|
||||
from materia_server.models.database import (
|
||||
Database,
|
||||
DatabaseError,
|
||||
DatabaseMigrationError,
|
||||
Cache,
|
||||
CacheError,
|
||||
)
|
||||
|
||||
from materia_server.models.user import User, UserCredentials, UserInfo
|
||||
|
||||
from materia_server.models.repository import Repository, RepositoryInfo
|
||||
from materia_server.models.repository import (
|
||||
Repository,
|
||||
RepositoryInfo,
|
||||
RepositoryContent,
|
||||
)
|
||||
|
||||
from materia_server.models.directory import Directory, DirectoryLink, DirectoryInfo
|
||||
|
||||
|
@ -59,7 +59,7 @@ class Directory(Base):
|
||||
|
||||
async def remove(self, db: database.Database):
|
||||
async with db.session() as session:
|
||||
await session.execute(sa.delete(Directory).where(Directory.id == self.id))
|
||||
await session.delete(self)
|
||||
await session.commit()
|
||||
|
||||
|
||||
|
@ -56,7 +56,7 @@ class File(Base):
|
||||
|
||||
async def remove(self, db: database.Database):
|
||||
async with db.session() as session:
|
||||
await session.execute(sa.delete(File).where(File.id == self.id))
|
||||
await session.delete(self)
|
||||
await session.commit()
|
||||
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
from time import time
|
||||
from typing import List, Self
|
||||
from typing import List, Self, Optional
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
from sqlalchemy import BigInteger, ForeignKey
|
||||
from sqlalchemy.orm import mapped_column, Mapped, relationship
|
||||
from sqlalchemy.orm.attributes import InstrumentedAttribute
|
||||
import sqlalchemy as sa
|
||||
from pydantic import BaseModel
|
||||
from pydantic import BaseModel, ConfigDict
|
||||
|
||||
from materia_server.models.base import Base
|
||||
from materia_server.models import database
|
||||
@ -55,15 +55,24 @@ class Repository(Base):
|
||||
|
||||
async def remove(self, db: database.Database):
|
||||
async with db.session() as session:
|
||||
await session.execute(sa.delete(Repository).where(Repository.id == self.id))
|
||||
await session.delete(self)
|
||||
await session.commit()
|
||||
|
||||
|
||||
class RepositoryInfo(BaseModel):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
capacity: int
|
||||
used: int
|
||||
used: Optional[int] = None
|
||||
|
||||
|
||||
class RepositoryContent(BaseModel):
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
files: list["FileInfo"]
|
||||
directories: list["DirectoryInfo"]
|
||||
|
||||
|
||||
from materia_server.models.user import User
|
||||
from materia_server.models.directory import Directory
|
||||
from materia_server.models.file import File
|
||||
from materia_server.models.directory import Directory, DirectoryInfo
|
||||
from materia_server.models.file import File, FileInfo
|
||||
|
@ -82,7 +82,7 @@ class User(Base):
|
||||
|
||||
async def remove(self, db: database.Database):
|
||||
async with db.session() as session:
|
||||
await session.execute(sa.delete(User).where(User.id == self.id))
|
||||
await session.delete(self)
|
||||
await session.commit()
|
||||
|
||||
|
||||
|
@ -28,36 +28,39 @@ async def create(
|
||||
session.add(user)
|
||||
await session.refresh(user, attribute_names=["repository"])
|
||||
|
||||
if not user.repository:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, "Repository not found")
|
||||
if not user.repository:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, "Repository not found")
|
||||
|
||||
current_directory = None
|
||||
current_path = Path()
|
||||
directory = None
|
||||
current_directory = None
|
||||
current_path = Path()
|
||||
directory = None
|
||||
|
||||
for part in directory_path.parts:
|
||||
if not await Directory.by_path(
|
||||
user.repository.id, current_path, part, ctx.database
|
||||
):
|
||||
directory = Directory(
|
||||
repository_id=user.repository.id,
|
||||
parent_id=current_directory.id if current_directory else None,
|
||||
name=part,
|
||||
path=None if current_path == Path() else str(current_path),
|
||||
)
|
||||
session.add(directory)
|
||||
|
||||
current_directory = directory
|
||||
current_path /= part
|
||||
|
||||
try:
|
||||
(repository_path / directory_path).mkdir(parents=True, exist_ok=True)
|
||||
except OSError:
|
||||
raise HTTPException(
|
||||
status.HTTP_500_INTERNAL_SERVER_ERROR, "Failed to created a directory"
|
||||
for part in directory_path.parts:
|
||||
if not await Directory.by_path(
|
||||
user.repository.id, current_path, part, ctx.database
|
||||
):
|
||||
directory = Directory(
|
||||
repository_id=user.repository.id,
|
||||
parent_id=current_directory.id if current_directory else None,
|
||||
name=part,
|
||||
path=None if current_path == Path() else str(current_path),
|
||||
)
|
||||
|
||||
await session.commit()
|
||||
try:
|
||||
(repository_path / current_path / part).mkdir(exist_ok=True)
|
||||
except OSError:
|
||||
raise HTTPException(
|
||||
status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
f"Failed to create a directory {current_path / part}",
|
||||
)
|
||||
|
||||
async with ctx.database.session() as session:
|
||||
session.add(directory)
|
||||
await session.commit()
|
||||
await session.refresh(directory)
|
||||
|
||||
current_directory = directory
|
||||
current_path /= part
|
||||
|
||||
|
||||
@router.get("/directory")
|
||||
|
@ -1,7 +1,15 @@
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
|
||||
from materia_server.models import User, Repository, RepositoryInfo
|
||||
from materia_server.models import (
|
||||
User,
|
||||
Repository,
|
||||
RepositoryInfo,
|
||||
RepositoryContent,
|
||||
FileInfo,
|
||||
DirectoryInfo,
|
||||
)
|
||||
from materia_server.routers import middleware
|
||||
from materia_server.config import Config
|
||||
|
||||
@ -32,35 +40,24 @@ async def create(
|
||||
|
||||
@router.get("/repository", response_model=RepositoryInfo)
|
||||
async def info(
|
||||
user: User = Depends(middleware.user), ctx: middleware.Context = Depends()
|
||||
repository=Depends(middleware.repository), ctx: middleware.Context = Depends()
|
||||
):
|
||||
async with ctx.database.session() as session:
|
||||
session.add(user)
|
||||
await session.refresh(user, attribute_names=["repository"])
|
||||
|
||||
if not (repository := user.repository):
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, "Repository not found")
|
||||
|
||||
async with ctx.database.session() as session:
|
||||
session.add(repository)
|
||||
await session.refresh(repository, attribute_names=["files"])
|
||||
|
||||
return RepositoryInfo(
|
||||
capacity=repository.capacity,
|
||||
used=sum([file.size for file in repository.files]),
|
||||
)
|
||||
info = RepositoryInfo.model_validate(repository)
|
||||
info.used = sum([file.size for file in repository.files])
|
||||
|
||||
return info
|
||||
|
||||
|
||||
@router.delete("/repository")
|
||||
async def remove(
|
||||
user: User = Depends(middleware.user), ctx: middleware.Context = Depends()
|
||||
repository=Depends(middleware.repository),
|
||||
repository_path=Depends(middleware.repository_path),
|
||||
ctx: middleware.Context = Depends(),
|
||||
):
|
||||
repository_path = Config.data_dir() / "repository" / user.lower_name
|
||||
|
||||
async with ctx.database.session() as session:
|
||||
session.add(user)
|
||||
await session.refresh(user, attribute_names=["repository"])
|
||||
|
||||
try:
|
||||
if repository_path.exists():
|
||||
shutil.rmtree(str(repository_path))
|
||||
@ -69,4 +66,33 @@ async def remove(
|
||||
status.HTTP_500_INTERNAL_SERVER_ERROR, "Failed to remove repository"
|
||||
)
|
||||
|
||||
await user.repository.remove(ctx.database)
|
||||
await repository.remove(ctx.database)
|
||||
|
||||
|
||||
@router.get("/repository/content", response_model=RepositoryContent)
|
||||
async def content(
|
||||
repository=Depends(middleware.repository), ctx: middleware.Context = Depends()
|
||||
):
|
||||
async with ctx.database.session() as session:
|
||||
session.add(repository)
|
||||
await session.refresh(repository, attribute_names=["directories"])
|
||||
await session.refresh(repository, attribute_names=["files"])
|
||||
|
||||
content = RepositoryContent(
|
||||
files=list(
|
||||
map(
|
||||
lambda file: FileInfo.model_validate(file),
|
||||
filter(lambda file: file.path is None, repository.files),
|
||||
)
|
||||
),
|
||||
directories=list(
|
||||
map(
|
||||
lambda directory: DirectoryInfo.model_validate(directory),
|
||||
filter(
|
||||
lambda directory: directory.path is None, repository.directories
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
return content
|
||||
|
@ -1,67 +1,81 @@
|
||||
from typing import Optional, Sequence
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from fastapi import HTTPException, Request, Response, status, Depends, Cookie
|
||||
from fastapi.security.base import SecurityBase
|
||||
import jwt
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import select
|
||||
from pydantic import BaseModel
|
||||
from enum import StrEnum
|
||||
from http import HTTPMethod as HttpMethod
|
||||
from fastapi.security import HTTPBearer, OAuth2PasswordBearer, OAuth2PasswordRequestForm, APIKeyQuery, APIKeyCookie, APIKeyHeader
|
||||
from fastapi.security import (
|
||||
HTTPBearer,
|
||||
OAuth2PasswordBearer,
|
||||
OAuth2PasswordRequestForm,
|
||||
APIKeyQuery,
|
||||
APIKeyCookie,
|
||||
APIKeyHeader,
|
||||
)
|
||||
|
||||
from materia_server import security
|
||||
from materia_server.models import User
|
||||
from materia_server.models import User, Repository
|
||||
|
||||
|
||||
class Context:
|
||||
def __init__(self, request: Request):
|
||||
self.config = request.state.config
|
||||
self.database = request.state.database
|
||||
self.cache = request.state.cache
|
||||
self.database = request.state.database
|
||||
self.cache = request.state.cache
|
||||
self.logger = request.state.logger
|
||||
|
||||
|
||||
async def jwt_cookie(request: Request, response: Response, ctx: Context = Depends()):
|
||||
if not (access_token := request.cookies.get(ctx.config.security.cookie_access_token_name)):
|
||||
if not (
|
||||
access_token := request.cookies.get(
|
||||
ctx.config.security.cookie_access_token_name
|
||||
)
|
||||
):
|
||||
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Missing token")
|
||||
refresh_token = request.cookies.get(ctx.config.security.cookie_refresh_token_name)
|
||||
|
||||
if ctx.config.oauth2.jwt_signing_algo in ["HS256", "HS384", "HS512"]:
|
||||
secret = ctx.config.oauth2.jwt_secret
|
||||
secret = ctx.config.oauth2.jwt_secret
|
||||
else:
|
||||
secret = ctx.config.oauth2.jwt_signing_key
|
||||
|
||||
issuer = "{}://{}".format(ctx.config.server.scheme, ctx.config.server.domain)
|
||||
|
||||
try:
|
||||
refresh_claims = security.validate_token(refresh_token, secret) if refresh_token else None
|
||||
|
||||
refresh_claims = (
|
||||
security.validate_token(refresh_token, secret) if refresh_token else None
|
||||
)
|
||||
|
||||
if refresh_claims:
|
||||
if refresh_claims.exp < datetime.now().timestamp():
|
||||
refresh_claims = None
|
||||
except jwt.PyJWTError:
|
||||
refresh_claims = None
|
||||
refresh_claims = None
|
||||
|
||||
try:
|
||||
access_claims = security.validate_token(access_token, secret)
|
||||
|
||||
|
||||
if access_claims.exp < datetime.now().timestamp():
|
||||
if refresh_claims:
|
||||
new_access_token = security.generate_token(
|
||||
access_claims.sub,
|
||||
access_claims.sub,
|
||||
str(secret),
|
||||
ctx.config.oauth2.access_token_lifetime,
|
||||
issuer
|
||||
issuer,
|
||||
)
|
||||
access_claims = security.validate_token(new_access_token, secret)
|
||||
response.set_cookie(
|
||||
ctx.config.security.cookie_access_token_name,
|
||||
value = new_access_token,
|
||||
max_age = ctx.config.oauth2.access_token_lifetime,
|
||||
secure = True,
|
||||
httponly = ctx.config.security.cookie_http_only,
|
||||
samesite = "lax"
|
||||
ctx.config.security.cookie_access_token_name,
|
||||
value=new_access_token,
|
||||
max_age=ctx.config.oauth2.access_token_lifetime,
|
||||
secure=True,
|
||||
httponly=ctx.config.security.cookie_http_only,
|
||||
samesite="lax",
|
||||
)
|
||||
else:
|
||||
access_claims = None
|
||||
@ -74,8 +88,23 @@ async def jwt_cookie(request: Request, response: Response, ctx: Context = Depend
|
||||
return access_claims
|
||||
|
||||
|
||||
async def user(claims = Depends(jwt_cookie), ctx: Context = Depends()):
|
||||
async def user(claims=Depends(jwt_cookie), ctx: Context = Depends()) -> User:
|
||||
if not (current_user := await User.by_id(uuid.UUID(claims.sub), ctx.database)):
|
||||
raise HTTPException(status.HTTP_401_UNAUTHORIZED, "Missing user")
|
||||
|
||||
return current_user
|
||||
|
||||
|
||||
async def repository(user: User = Depends(user), ctx: Context = Depends()):
|
||||
async with ctx.database.session() as session:
|
||||
session.add(user)
|
||||
await session.refresh(user, attribute_names=["repository"])
|
||||
|
||||
if not (repository := user.repository):
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, "Repository not found")
|
||||
|
||||
return repository
|
||||
|
||||
|
||||
async def repository_path(user: User = Depends(user), ctx: Context = Depends()) -> Path:
|
||||
return ctx.config.data_dir() / "repository" / user.lower_name
|
||||
|
Loading…
Reference in New Issue
Block a user