Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 4 additions & 28 deletions bin/freenit.sh
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,10 @@ EOF
await store.auth.logout()
}

onMount(async () => { await store.auth.refresh_token() })
onMount(async () => {
await store.loadModules()
await store.auth.refresh_token()
})
</script>

<svelte:head>
Expand Down Expand Up @@ -585,33 +588,6 @@ EOF
</script>

<Domains store={store} />
EOF

mkdir -p 'src/routes/themes/[pk]'
cat >'src/routes/themes/[pk]/+page.ts' <<EOF
export const load = ({ params }) => {
return {
name: params.name
}
}
EOF
cat >'src/routes/themes/[pk]/+page.svelte' <<EOF
<script lang="ts">
import { Theme } from 'freenit'
import store from '\$lib/store'

const { data: props } = \$props()
</script>

<Theme name={props.name} store={store} />
EOF
cat >'src/routes/themes/+page.svelte' <<EOF
<script lang="ts">
import { Themes } from 'freenit'
import store from '\$lib/store'
</script>

<Themes store={store} />
EOF

mkdir -p 'src/routes/users/[pk]'
Expand Down
2 changes: 1 addition & 1 deletion freenit/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.3.27"
__version__ = "0.3.28"
42 changes: 30 additions & 12 deletions freenit/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
import freenit.api.auth
import freenit.api.dav
import freenit.api.domain
import freenit.api.jabber
import freenit.api.mail
import freenit.api.mailinglist
import freenit.api.omemo
import freenit.api.project
import freenit.api.role
import freenit.api.sieve
import freenit.api.theme
import freenit.api.user
from importlib import import_module

from freenit.config import getConfig
from freenit.modules import MODULES, get_api_modules, resolve_modules

from .router import api

config = getConfig()

# Resolve the configured modules (including dependencies) and import their API
# modules. Each API module registers its routes on the shared `api` app as an
# import side-effect, preserving the existing decorator-based registration.
_module_names = resolve_modules(config.modules)
for _api_module in get_api_modules(config.modules):
import_module(_api_module)


@api.get("/")
async def api_root():
"""Discovery endpoint: list active modules.

The frontend uses this response to decide which features to expose.
"""
return {
"modules": sorted(_module_names),
"meta": {
name: {
"dependencies": MODULES[name].dependencies,
}
for name in _module_names
},
}
43 changes: 33 additions & 10 deletions freenit/api/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ class TaskResponse(pydantic.BaseModel):
updated_at: datetime | None = None


class TaskDetailResponse(TaskResponse):
children: list[TaskResponse] = []
parent: TaskResponse | None = None


class ProjectGroupCreate(pydantic.BaseModel):
name: str
description: str | None = None
Expand Down Expand Up @@ -261,9 +266,7 @@ async def _get_project_group_permissions(group_id: int) -> list[str]:
return [p.permission for p in permissions]


async def _set_project_group_permissions(
group_id: int, permissions: list[str]
) -> None:
async def _set_project_group_permissions(group_id: int, permissions: list[str]) -> None:
existing = await ProjectGroupPermission.objects.filter(group_id=group_id).all()
for perm in existing:
await perm.delete()
Expand Down Expand Up @@ -524,9 +527,19 @@ async def post(
@route("/tasks/{id}", tags=tags)
class TaskDetailAPI:
@staticmethod
async def get(id: int, _: User = Depends(project_perms)) -> TaskResponse:
async def get(id: int, _: User = Depends(project_perms)) -> TaskDetailResponse:
task = await _get_task(id)
return TaskResponse.model_validate(task)
children = await Task.objects.filter(parent_id=id).order_by("position").all()
parent = None
if task.parent_id is not None:
try:
parent = await Task.objects.get(id=task.parent_id)
except oxyde.NotFoundError:
parent = None
data = TaskResponse.model_validate(task).model_dump()
data["children"] = [TaskResponse.model_validate(child) for child in children]
data["parent"] = TaskResponse.model_validate(parent) if parent else None
return TaskDetailResponse(**data)

@staticmethod
async def patch(
Expand Down Expand Up @@ -588,7 +601,9 @@ async def get(
perpage,
)
for item in result.data:
object.__setattr__(item, "permissions", await _get_project_group_permissions(item.id))
object.__setattr__(
item, "permissions", await _get_project_group_permissions(item.id)
)
return result

@staticmethod
Expand All @@ -609,7 +624,9 @@ async def post(
updated_at=now,
)
await _set_project_group_permissions(group.id, data.permissions)
object.__setattr__(group, "permissions", await _get_project_group_permissions(group.id))
object.__setattr__(
group, "permissions", await _get_project_group_permissions(group.id)
)
return ProjectGroupResponse.model_validate(group)


Expand All @@ -618,7 +635,9 @@ class ProjectGroupDetailAPI:
@staticmethod
async def get(id: int, _: User = Depends(project_perms)) -> ProjectGroupResponse:
group = await _get_project_group(id)
object.__setattr__(group, "permissions", await _get_project_group_permissions(id))
object.__setattr__(
group, "permissions", await _get_project_group_permissions(id)
)
return ProjectGroupResponse.model_validate(group)

@staticmethod
Expand All @@ -629,13 +648,17 @@ async def patch(
) -> ProjectGroupResponse:
group = await _get_project_group(id)
if data.name:
await _check_project_group_name_unique(group.project_id, data.name, exclude_id=id)
await _check_project_group_name_unique(
group.project_id, data.name, exclude_id=id
)
update = data.model_dump(exclude_none=True)
if "permissions" in update:
await _set_project_group_permissions(id, update.pop("permissions"))
if update:
await group.patch(ProjectGroupOptional(**update))
object.__setattr__(group, "permissions", await _get_project_group_permissions(id))
object.__setattr__(
group, "permissions", await _get_project_group_permissions(id)
)
return ProjectGroupResponse.model_validate(group)

@staticmethod
Expand Down
86 changes: 0 additions & 86 deletions freenit/api/theme.py

This file was deleted.

29 changes: 25 additions & 4 deletions freenit/base_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,14 +134,13 @@ class BaseConfig:
port = 5000
debug = False
dburl = "sqlite:///db.sqlite"
database = None
_database = None
secret = "SECRET" # nosec
user = "freenit.models.sql.user"
role = "freenit.models.sql.role"
theme = "freenit.models.sql.theme"
mailinglist = "freenit.models.sql.mailinglist"
project = "freenit.models.sql.project"
theme_name = "Freenit"
modules = ["auth"]
meta = None
auth = Auth()
mail = None
Expand All @@ -157,7 +156,16 @@ def __init__(self):
dbpath = Path(dburl.removeprefix("sqlite:///")).resolve()
dburl = f"sqlite:///{dbpath}"
self.dburl = dburl
self.database = oxyde.AsyncDatabase(self.dburl, overwrite=True)

@property
def database(self):
if self._database is None:
self._database = oxyde.AsyncDatabase(self.dburl, overwrite=True)
return self._database

@database.setter
def database(self, value):
self._database = value

def __repr__(self):
return (
Expand Down Expand Up @@ -186,6 +194,19 @@ class TestConfig(BaseConfig):
debug = True
dburl = "sqlite:///test.sqlite"
auth = Auth(secure=False)
modules = [
"auth",
"user",
"role",
"project",
"mailinglist",
"domain",
"dav",
"mail",
"sieve",
"jabber",
"omemo",
]


class ProdConfig(BaseConfig):
Expand Down
Loading
Loading