Merge pull request #89 from plers-org/sm/#88

Sm/#88
This commit is contained in:
NKEY
2025-08-21 00:34:10 +09:00
committed by GitHub
4 changed files with 53 additions and 3 deletions

View File

@@ -125,6 +125,7 @@ CACHES = {
'default': { 'default': {
'BACKEND': 'django_redis.cache.RedisCache', 'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': f'redis://:{REDIS_PASSWORD}@redis:6379/1', 'LOCATION': f'redis://:{REDIS_PASSWORD}@redis:6379/1',
# 'LOCATION': f'redis://127.0.0.1:6379/1',
'OPTIONS': { 'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient', 'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'SERIALIZER': 'django_redis.serializers.json.JSONSerializer', 'SERIALIZER': 'django_redis.serializers.json.JSONSerializer',

View File

@@ -3,8 +3,26 @@ from rest_framework.permissions import BasePermission
from projects.models import Project, ProjectTeamList from projects.models import Project, ProjectTeamList
from portfolios.models import Portfolio from portfolios.models import Portfolio
from .services import NocodetoolObjectMapService
UNSAFE_REQUEST = ["POST", "PUT", "PATCH", "DELETE"] UNSAFE_REQUEST = ["POST", "PUT", "PATCH", "DELETE"]
class IsNotPublished(BasePermission):
def has_permission(self, request, view):
if request.method not in UNSAFE_REQUEST:
return True
related_type = request.query_params.get("type")
related_id = request.query_params.get("id")
if not related_type or not related_id:
return False
if obj := NocodetoolObjectMapService.mapping_model_instance(related_type, related_id):
if not obj.is_published:
return True
return False
class IsOwnerOrMemberInCreateAndUpdateAndDelete(BasePermission): class IsOwnerOrMemberInCreateAndUpdateAndDelete(BasePermission):
def has_permission(self, request, view): def has_permission(self, request, view):
if request.method not in UNSAFE_REQUEST: if request.method not in UNSAFE_REQUEST:

View File

@@ -7,6 +7,11 @@ from portfolios.models import Portfolio
from projects.serializers import ProjectNocodetoolSerializer from projects.serializers import ProjectNocodetoolSerializer
from portfolios.serializers import PortfolioNocodetoolSerializer from portfolios.serializers import PortfolioNocodetoolSerializer
from django.utils import timezone
from django.db.models import F
from django_redis import get_redis_connection
NOCODETOOL_MODEL_MAP = { NOCODETOOL_MODEL_MAP = {
'project': Project, 'project': Project,
'portfolio': Portfolio, 'portfolio': Portfolio,
@@ -27,3 +32,26 @@ class NocodetoolObjectMapService:
def mapping_model_serializer(related_type: str): def mapping_model_serializer(related_type: str):
return NOCODETOOL_SERIALIZER_MAP.get(related_type, None) return NOCODETOOL_SERIALIZER_MAP.get(related_type, None)
class NocodetoolHitService:
def hit_once(obj, request, ttl=60*60*24):
user_key = request.user.id if request.user.is_authenticated else request.session.session_key
if not user_key:
request.session.save()
user_key = request.session.session_key()
today = timezone.localdate().isoformat()
key = f"viewed:obj:{obj.id}:{today}"
redis_conn = get_redis_connection("default")
added = redis_conn.sadd(key, user_key)
if added:
if redis_conn.ttl(key) == -1:
redis_conn.expire(key, ttl)
obj.__class__.objects.filter(id=obj.id).update(view_count=F("view_count") + 1)
return True
return False

View File

@@ -7,8 +7,8 @@ from django.db import transaction
from .models import Code, Page, Element from .models import Code, Page, Element
from .serializers import CodeSerializer from .serializers import CodeSerializer
from .permissions import IsOwnerOrMemberInCreateAndUpdateAndDelete from .permissions import IsOwnerOrMemberInCreateAndUpdateAndDelete, IsNotPublished
from .services import NocodetoolObjectMapService from .services import NocodetoolObjectMapService, NocodetoolHitService
from users.models import User from users.models import User
from portfolios.models import Portfolio from portfolios.models import Portfolio
@@ -18,13 +18,16 @@ from bson import ObjectId
class NoCodeToolAPIView(APIView): class NoCodeToolAPIView(APIView):
permission_classes = [IsAuthenticated, IsOwnerOrMemberInCreateAndUpdateAndDelete] permission_classes = [IsAuthenticated, IsNotPublished, IsOwnerOrMemberInCreateAndUpdateAndDelete]
@transaction.atomic
def get(self, request): def get(self, request):
related_type = request.query_params.get("type") related_type = request.query_params.get("type")
related_id = request.query_params.get("id") related_id = request.query_params.get("id")
code_id = None code_id = None
if obj := NocodetoolObjectMapService.mapping_model_instance(related_type, related_id): if obj := NocodetoolObjectMapService.mapping_model_instance(related_type, related_id):
if obj.is_published:
NocodetoolHitService.hit_once(obj, request)
code_id = ObjectId(obj.code_id) code_id = ObjectId(obj.code_id)
if not code_id: if not code_id: