adjust registered by to managed by on db aslo remove url and api key on adding sites for gm

This commit is contained in:
Alberto-Audrix committed 2026-09-11 16:18:04 +07:00
1 parent 64b37b6b6d
commit 521f36d22d
15 files changed
+112 -75

No files matched your search

+2 -2
View File
@@ -17,8 +17,8 @@ Main-office **mirror API** for the executive frontend. Domain shapes match site
`bootstrap_admin` / seed `admin` uses status `superuser`. This app does not use `is_staff`.
GM↔BUH link: `User.managed_by` (confirmed). **Only `gm`** may request a **`buh`** (not `bu_admin` / `gm_admin`); **only that `buh`** may approve.
Active site registry ownership: `ActiveSite.registered_by`.
GM farm visibility follows the Pengaturan active-site list: only HQ `Site` rows linked to an enabled registered `ActiveSite` are shown (empty list ⇒ no sites).
Active site registry ownership: `ActiveSite.managed_by`.
GM farm visibility follows the Pengaturan active-site list: only HQ `Site` rows linked to an enabled `ActiveSite` they manage are shown (empty list ⇒ no sites).
BUH farm visibility is the union of those lists for **confirmed** managed GMs (read-only Pengaturan rollup; optional GM filter in the header).
`POST /api/v1/cycles/{id}/approve-close/` — GM only.
+5 -5
View File
@@ -2,9 +2,9 @@
- director / director_admin: all sites / active sites / GMs
- buh / bu_admin: only GMs (and gm_admins) they manage, plus those GMs' sites
- gm / gm_admin: only their own sites / active sites they registered
- gm / gm_admin: only their own sites / active sites they manage
Farm Site rows are only visible to non-directors when linked to an active
Farm Site rows are only visible to non-directors when linked to an enabled
ActiveSite in the viewer's Pengaturan registry.
"""
@@ -61,7 +61,7 @@ def managed_gm_ids(buh: User) -> list[int]:
def visible_owner_ids(user: User) -> list[int] | None:
"""
Site.user / ActiveSite.registered_by ids this viewer may see.
Site.user / ActiveSite.managed_by ids this viewer may see.
Returns None when there is no restriction (director / superuser).
"""
@@ -89,7 +89,7 @@ def visible_active_site_ids(user: User) -> list[int] | None:
return []
return list(
ActiveSite.objects.filter(
registered_by_id__in=owners,
managed_by_id__in=owners,
is_active=True,
).values_list("pk", flat=True)
)
@@ -132,7 +132,7 @@ def filter_active_sites_queryset(qs: QuerySet, viewer: User) -> QuerySet:
return qs
if not owners:
return qs.none()
return qs.filter(registered_by_id__in=owners)
return qs.filter(managed_by_id__in=owners)
def filter_kandangs_queryset(qs: QuerySet, viewer: User) -> QuerySet:
+2 -3
View File
@@ -31,13 +31,13 @@ class VisibilityScopeTests(TestCase):
code="a",
name="Site A",
api_base_url="https://a.example/api/v1",
registered_by=self.gm,
managed_by=self.gm,
)
self.active_b = ActiveSite.objects.create(
code="b",
name="Site B",
api_base_url="https://b.example/api/v1",
registered_by=self.gm_other,
managed_by=self.gm_other,
)
self.site = Site.objects.create(
site_name="Site A", user=self.gm, active_site=self.active_a
@@ -98,7 +98,6 @@ class VisibilityScopeTests(TestCase):
{
"code": "orphan-seed",
"name": "Orphan Seed",
"api_base_url": "https://orphan.example/api/v1",
"is_active": True,
},
format="json",
@@ -18,9 +18,9 @@ def ensure_hq_site_for_active_site(active_site: ActiveSite) -> Site:
if existing:
return existing
owner = active_site.registered_by
owner = active_site.managed_by
if owner is None:
raise ValueError("ActiveSite.registered_by is required to create an HQ site.")
raise ValueError("ActiveSite.managed_by is required to create an HQ site.")
site_name = (active_site.name or active_site.code or "Site")[:30]
orphan = (
+2 -2
View File
@@ -11,12 +11,12 @@ from apps.sync.models import ActiveSite
def _gm_visible_site(*, owner: User, site_name: str, code: str) -> Site:
"""GM scope only sees sites linked to an ActiveSite they registered."""
"""GM scope only sees sites linked to an ActiveSite they manage."""
active_site = ActiveSite.objects.create(
code=code,
name=site_name,
api_base_url=f"https://{code}.example/api/v1",
registered_by=owner,
managed_by=owner,
)
return Site.objects.create(
site_name=site_name, user=owner, active_site=active_site
+1 -1
View File
@@ -48,7 +48,7 @@ def _upsert_mirror_cycle_from_ingest(active_site: ActiveSite, payload: dict) ->
site_name = str(payload.get("site_name") or active_site.name)[:30]
kandang_name = str(payload.get("kandang_name") or "Kandang")[:30]
owner = active_site.registered_by
owner = active_site.managed_by
if owner is None:
owner = User.objects.filter(status=User.STATUS_GM).first()
if owner is None:
@@ -108,8 +108,8 @@ class Command(BaseCommand):
if not staff:
return
moved_sites = Site.objects.filter(user=staff).update(user=gm)
moved_active = ActiveSite.objects.filter(registered_by=staff).update(
registered_by=gm
moved_active = ActiveSite.objects.filter(managed_by=staff).update(
managed_by=gm
)
ApiKey.objects.filter(user=staff).delete()
staff.delete()
@@ -0,0 +1,36 @@
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("sync", "0003_rename_citysite_to_activesite"),
]
operations = [
migrations.AlterField(
model_name="activesite",
name="api_base_url",
field=models.URLField(
blank=True,
default="",
help_text="Optional site API root including /api/v1 (for outbound sync/push)",
max_length=255,
),
),
migrations.AlterField(
model_name="activesite",
name="registered_by",
field=models.ForeignKey(
blank=True,
help_text="GM / GM Admin who registered this site by name and code",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="registered_active_sites",
to=settings.AUTH_USER_MODEL,
),
),
]
@@ -0,0 +1,33 @@
# Generated manually: rename ActiveSite.registered_by → managed_by
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("sync", "0004_activesite_api_base_url_optional"),
]
operations = [
migrations.RenameField(
model_name="activesite",
old_name="registered_by",
new_name="managed_by",
),
migrations.AlterField(
model_name="activesite",
name="managed_by",
field=models.ForeignKey(
blank=True,
help_text="GM / GM Admin who manages this active site",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="managed_active_sites",
to=settings.AUTH_USER_MODEL,
),
),
]
+6 -4
View File
@@ -17,20 +17,22 @@ class ActiveSite(models.Model):
name = models.CharField(max_length=120)
api_base_url = models.URLField(
max_length=255,
help_text="Site API root including /api/v1, e.g. https://site.example/api/v1",
blank=True,
default="",
help_text="Optional site API root including /api/v1 (for outbound sync/push)",
)
api_key = models.CharField(
max_length=255,
blank=True,
help_text="X-API-Key for pulling from the site API (keep server-side only)",
)
registered_by = models.ForeignKey(
managed_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="registered_active_sites",
help_text="GM / GM Admin who added this site dashboard URL",
related_name="managed_active_sites",
help_text="GM / GM Admin who manages this active site",
)
is_active = models.BooleanField(default=True)
last_synced_at = models.DateTimeField(null=True, blank=True)
+6 -6
View File
@@ -7,8 +7,8 @@ class ActiveSiteSerializer(serializers.ModelSerializer):
id = serializers.IntegerField(source="pk", read_only=True)
# Never echo the outbound site API key back to the browser by default.
has_api_key = serializers.SerializerMethodField()
registered_by_name = serializers.CharField(
source="registered_by.display_name", read_only=True, allow_null=True
managed_by_name = serializers.CharField(
source="managed_by.display_name", read_only=True, allow_null=True
)
class Meta:
@@ -19,8 +19,8 @@ class ActiveSiteSerializer(serializers.ModelSerializer):
"name",
"api_base_url",
"has_api_key",
"registered_by",
"registered_by_name",
"managed_by",
"managed_by_name",
"is_active",
"last_synced_at",
"last_sync_status",
@@ -31,8 +31,8 @@ class ActiveSiteSerializer(serializers.ModelSerializer):
read_only_fields = [
"id",
"has_api_key",
"registered_by",
"registered_by_name",
"managed_by",
"managed_by_name",
"last_synced_at",
"last_sync_status",
"last_sync_message",
+2 -2
View File
@@ -22,7 +22,7 @@ class ActiveSiteViewSet(viewsets.ModelViewSet):
executive_sync_action = True
def get_queryset(self):
qs = ActiveSite.objects.select_related("registered_by").all()
qs = ActiveSite.objects.select_related("managed_by").all()
return filter_active_sites_queryset(qs, self.request.user)
def get_permissions(self):
@@ -36,7 +36,7 @@ class ActiveSiteViewSet(viewsets.ModelViewSet):
return ActiveSiteSerializer
def perform_create(self, serializer):
active_site = serializer.save(registered_by=self.request.user)
active_site = serializer.save(managed_by=self.request.user)
ensure_hq_site_for_active_site(active_site)
def perform_update(self, serializer):