replace DRF with django-ninja
This commit is contained in:
parent
4299d46b7b
commit
813780a18b
|
@ -0,0 +1,112 @@
|
||||||
|
from ninja import Router, Schema
|
||||||
|
from ninja.errors import HttpError
|
||||||
|
from concierge import models
|
||||||
|
from typing import List
|
||||||
|
from django.db import transaction
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
from django.utils.timezone import now
|
||||||
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
|
||||||
|
class ClaimReference(Schema):
|
||||||
|
uuid: str
|
||||||
|
|
||||||
|
|
||||||
|
class AvailableTask(Schema):
|
||||||
|
uuid: str
|
||||||
|
type: str
|
||||||
|
|
||||||
|
|
||||||
|
class HeartbeatResponse(Schema):
|
||||||
|
success: bool = True
|
||||||
|
claims: List[ClaimReference]
|
||||||
|
available: List[AvailableTask]
|
||||||
|
|
||||||
|
|
||||||
|
class ClaimResponse(Schema):
|
||||||
|
uuid: str
|
||||||
|
type: str
|
||||||
|
configuration: dict
|
||||||
|
|
||||||
|
|
||||||
|
class ReleaseResponse(Schema):
|
||||||
|
success: bool = True
|
||||||
|
uuid: str
|
||||||
|
type: str
|
||||||
|
|
||||||
|
|
||||||
|
@router.post('/heartbeat/{identity}', response=HeartbeatResponse)
|
||||||
|
@csrf_exempt
|
||||||
|
def receive_heartbeat(request, identity: str):
|
||||||
|
try:
|
||||||
|
id = models.Identity.objects.get(identity=identity)
|
||||||
|
except models.Identity.DoesNotExist:
|
||||||
|
raise HttpError(403, "identity unknown")
|
||||||
|
|
||||||
|
# update heartbeat
|
||||||
|
id.heartbeat = now()
|
||||||
|
id.save()
|
||||||
|
|
||||||
|
# get current claims and available tasks
|
||||||
|
claims = models.Task.objects.filter(claimed_by=id).all()
|
||||||
|
available = models.Task.objects.filter(claimed_by=None).all()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'success': True,
|
||||||
|
'claims': [{'uuid': str(o.uuid)} for o in list(claims)],
|
||||||
|
'available': [{'uuid': str(o.uuid), 'type': o.type} for o in list(available)],
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post('/claim/{identity}/{task_uuid}', response=ClaimResponse)
|
||||||
|
@csrf_exempt
|
||||||
|
def claim_task(request, identity: str, task_uuid: str):
|
||||||
|
try:
|
||||||
|
id = models.Identity.objects.get(identity=identity)
|
||||||
|
except models.Identity.DoesNotExist:
|
||||||
|
raise HttpError(403, "identity unknown")
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
task = get_object_or_404(models.Task, uuid=task_uuid)
|
||||||
|
|
||||||
|
if task.claimed_by:
|
||||||
|
raise HttpError(423, "task already claimed")
|
||||||
|
|
||||||
|
task.claimed_by = id
|
||||||
|
task.save()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'success': True,
|
||||||
|
'uuid': task.uuid,
|
||||||
|
'type': task.type,
|
||||||
|
'configuration': json.loads(task.configuration)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post('/release/{identity}/{task_uuid}')
|
||||||
|
@csrf_exempt
|
||||||
|
def release_task(request, identity: str, task_uuid: str):
|
||||||
|
try:
|
||||||
|
id = models.Identity.objects.get(identity=identity)
|
||||||
|
except models.Identity.DoesNotExist:
|
||||||
|
raise HttpError(403, "identity unknown")
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
task = get_object_or_404(models.Task, uuid=task_uuid)
|
||||||
|
|
||||||
|
if task.claimed_by != id:
|
||||||
|
raise HttpError(403, "task not claimed by this identity")
|
||||||
|
|
||||||
|
task.claimed_by = None
|
||||||
|
task.save()
|
||||||
|
|
||||||
|
return {
|
||||||
|
'success': True,
|
||||||
|
'uuid': task.uuid,
|
||||||
|
'type': task.type,
|
||||||
|
}
|
|
@ -1,8 +0,0 @@
|
||||||
from django.urls import path
|
|
||||||
from . import views
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path('api/<uuid:identity>/heartbeat', views.heartbeat, name='heartbeat'),
|
|
||||||
path('api/<uuid:identity>/claim/<uuid:task_uuid>', views.claim, name='claim'),
|
|
||||||
path('api/<uuid:identity>/release/<uuid:task_uuid>', views.release, name='release'),
|
|
||||||
]
|
|
|
@ -1,94 +0,0 @@
|
||||||
import json
|
|
||||||
|
|
||||||
from django.db import transaction
|
|
||||||
from django.http import JsonResponse
|
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
|
||||||
from django.views.decorators.http import require_POST
|
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
|
||||||
from django.utils.timezone import now
|
|
||||||
|
|
||||||
from .models import Identity, Task
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
@require_POST
|
|
||||||
def heartbeat(request, identity):
|
|
||||||
try:
|
|
||||||
id = Identity.objects.get(identity=identity)
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
return JsonResponse({'error': 'identity unknown'}, status=403)
|
|
||||||
|
|
||||||
# update heartbeat
|
|
||||||
id.heartbeat = now()
|
|
||||||
id.save()
|
|
||||||
|
|
||||||
# get current claims and available tasks
|
|
||||||
claims = Task.objects.filter(claimed_by=id).all()
|
|
||||||
available = Task.objects.filter(claimed_by=None).all()
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'success': True,
|
|
||||||
'claims': [{'uuid': str(o.uuid)} for o in list(claims)],
|
|
||||||
'available': [{'uuid': str(o.uuid), 'type': o.type} for o in list(available)],
|
|
||||||
}
|
|
||||||
|
|
||||||
return JsonResponse(data)
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
@require_POST
|
|
||||||
def claim(request, identity, task_uuid):
|
|
||||||
try:
|
|
||||||
id = Identity.objects.get(identity=identity)
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
return JsonResponse({'error': 'identity unknown'}, status=403)
|
|
||||||
|
|
||||||
with transaction.atomic():
|
|
||||||
try:
|
|
||||||
task = Task.objects.get(uuid=task_uuid)
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
return JsonResponse({'error': 'task unknown'}, status=404)
|
|
||||||
|
|
||||||
if task.claimed_by:
|
|
||||||
return JsonResponse({'error': 'task already claimed'}, status=423)
|
|
||||||
|
|
||||||
task.claimed_by = id
|
|
||||||
task.save()
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'success': True,
|
|
||||||
'uuid': task.uuid,
|
|
||||||
'type': task.type,
|
|
||||||
'configuration': json.loads(task.configuration)
|
|
||||||
}
|
|
||||||
|
|
||||||
return JsonResponse(data)
|
|
||||||
|
|
||||||
|
|
||||||
@csrf_exempt
|
|
||||||
@require_POST
|
|
||||||
def release(request, identity, task_uuid):
|
|
||||||
try:
|
|
||||||
id = Identity.objects.get(identity=identity)
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
return JsonResponse({'error': 'identity unknown'}, status=403)
|
|
||||||
|
|
||||||
with transaction.atomic():
|
|
||||||
try:
|
|
||||||
task = Task.objects.get(uuid=task_uuid)
|
|
||||||
except ObjectDoesNotExist:
|
|
||||||
return JsonResponse({'error': 'task unknown'}, status=404)
|
|
||||||
|
|
||||||
if task.claimed_by != id:
|
|
||||||
return JsonResponse({'error': 'task claimed by other identity'}, status=403)
|
|
||||||
|
|
||||||
task.claimed_by = None
|
|
||||||
task.save()
|
|
||||||
|
|
||||||
data = {
|
|
||||||
'success': True,
|
|
||||||
'uuid': task.uuid,
|
|
||||||
'type': task.type,
|
|
||||||
}
|
|
||||||
|
|
||||||
return JsonResponse(data)
|
|
|
@ -0,0 +1,144 @@
|
||||||
|
from ninja import Router, ModelSchema, Schema
|
||||||
|
from ninja.errors import HttpError
|
||||||
|
from config import models
|
||||||
|
from typing import List
|
||||||
|
from guardian.shortcuts import get_objects_for_user, assign_perm
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
|
||||||
|
class Restream(ModelSchema):
|
||||||
|
class Meta:
|
||||||
|
model = models.Restream
|
||||||
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
|
class RestreamPatch(ModelSchema):
|
||||||
|
class Meta:
|
||||||
|
model = models.Restream
|
||||||
|
exclude = ["id"]
|
||||||
|
fields_optional = "__all__"
|
||||||
|
extra = "forbid"
|
||||||
|
|
||||||
|
|
||||||
|
class Stream(ModelSchema):
|
||||||
|
class Meta:
|
||||||
|
model = models.Stream
|
||||||
|
fields = "__all__"
|
||||||
|
|
||||||
|
|
||||||
|
class StreamPatch(ModelSchema):
|
||||||
|
class Meta:
|
||||||
|
model = models.Stream
|
||||||
|
fields = ["name"]
|
||||||
|
fields_optional = "__all__"
|
||||||
|
extra = "forbid"
|
||||||
|
|
||||||
|
|
||||||
|
class StreamCreate(ModelSchema):
|
||||||
|
class Meta:
|
||||||
|
model = models.Stream
|
||||||
|
fields = ["name"]
|
||||||
|
extra = "forbid"
|
||||||
|
|
||||||
|
|
||||||
|
@router.get('/streams', response=List[Stream])
|
||||||
|
def list_streams(request):
|
||||||
|
return get_objects_for_user(request.user, 'view_stream', models.Stream.objects.all())
|
||||||
|
|
||||||
|
|
||||||
|
@router.post('/streams', response=Stream)
|
||||||
|
def create_stream(request, payload: StreamCreate):
|
||||||
|
stream = models.Stream.objects.create(**payload.dict())
|
||||||
|
assign_perm('view_stream', request.user, stream)
|
||||||
|
assign_perm('change_stream', request.user, stream)
|
||||||
|
assign_perm('delete_stream', request.user, stream)
|
||||||
|
return stream
|
||||||
|
|
||||||
|
|
||||||
|
@router.get('/streams/{id}', response=Stream)
|
||||||
|
def get_stream(request, id: int):
|
||||||
|
stream = get_object_or_404(models.Stream, id=id)
|
||||||
|
|
||||||
|
if not request.user.has_perm('view_stream', stream):
|
||||||
|
raise HttpError(401, "unauthorized")
|
||||||
|
return stream
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch('/streams/{id}', response=Stream)
|
||||||
|
def update_stream(request, id: int, payload: StreamPatch):
|
||||||
|
stream = get_object_or_404(models.Stream, id=id)
|
||||||
|
|
||||||
|
if not request.user.has_perm('change_stream', stream):
|
||||||
|
raise HttpError(401, "unauthorized")
|
||||||
|
|
||||||
|
stream.name = payload.name
|
||||||
|
stream.save()
|
||||||
|
return stream
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete('/streams/{id}', response=None)
|
||||||
|
def delete_stream(request, id: int):
|
||||||
|
stream = get_object_or_404(models.Stream, id=id)
|
||||||
|
|
||||||
|
if not request.user.has_perm('delete_stream', stream):
|
||||||
|
raise HttpError(401, "unauthorized")
|
||||||
|
|
||||||
|
stream.delete()
|
||||||
|
|
||||||
|
|
||||||
|
@router.get('/restreams', response=List[Restream])
|
||||||
|
def list_restreams(request):
|
||||||
|
return get_objects_for_user(request.user, 'view_restream', models.Restream.objects.all())
|
||||||
|
|
||||||
|
|
||||||
|
@router.post('/restreams', response=Restream)
|
||||||
|
def create_restream(request, payload: Restream):
|
||||||
|
if not request.user.has_perm('view_stream', payload.stream):
|
||||||
|
raise HttpError(401, "unauthorized")
|
||||||
|
|
||||||
|
restream = models.Restream.objects.create(**payload.dict())
|
||||||
|
assign_perm('view_restream', request.user, restream)
|
||||||
|
assign_perm('change_restream', request.user, restream)
|
||||||
|
assign_perm('delete_restream', request.user, restream)
|
||||||
|
return restream
|
||||||
|
|
||||||
|
|
||||||
|
@router.get('/restreams/{id}', response=Restream)
|
||||||
|
def get_restream(request, id: int):
|
||||||
|
restream = get_object_or_404(models.Restream, id=id)
|
||||||
|
|
||||||
|
if not request.user.has_perm('view_restream', restream):
|
||||||
|
raise HttpError(401, "unauthorized")
|
||||||
|
|
||||||
|
return restream
|
||||||
|
|
||||||
|
|
||||||
|
@router.patch('/restreams/{id}', response=Restream)
|
||||||
|
def update_restream(request, id: int, payload: RestreamPatch):
|
||||||
|
restream = get_object_or_404(models.Restream, id=id)
|
||||||
|
|
||||||
|
if not request.user.has_perm('change_restream', restream):
|
||||||
|
raise HttpError(401, "unauthorized")
|
||||||
|
|
||||||
|
if payload.stream:
|
||||||
|
payload.stream = get_object_or_404(models.Stream, id=payload.stream)
|
||||||
|
|
||||||
|
if not request.user.has_perm('view_stream', payload.stream):
|
||||||
|
raise HttpError(401, "unauthorized")
|
||||||
|
|
||||||
|
for key, value in payload.dict(exclude_unset=True).items():
|
||||||
|
setattr(restream, key, value)
|
||||||
|
restream.save()
|
||||||
|
return restream
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete('/restreams/{id}', response=None)
|
||||||
|
def delete_restream(request, id: int):
|
||||||
|
restream = get_object_or_404(models.Restream, id=id)
|
||||||
|
|
||||||
|
if not request.user.has_perm('delete_restream', restream):
|
||||||
|
raise HttpError(401, "unauthorized")
|
||||||
|
|
||||||
|
restream.delete()
|
|
@ -0,0 +1,30 @@
|
||||||
|
from ninja import NinjaAPI, ModelSchema, Router
|
||||||
|
from ninja.security import django_auth
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
|
||||||
|
from config.api import router as config_router
|
||||||
|
from concierge.api import router as concierge_router
|
||||||
|
|
||||||
|
core_router = Router()
|
||||||
|
|
||||||
|
class UserSchema(ModelSchema):
|
||||||
|
class Meta:
|
||||||
|
model = User
|
||||||
|
fields = ["id", "username", "email"]
|
||||||
|
|
||||||
|
@core_router.get("/me", response=UserSchema)
|
||||||
|
def me(request):
|
||||||
|
return request.user
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
api = NinjaAPI(
|
||||||
|
title="Portier API",
|
||||||
|
version="2.0.0",
|
||||||
|
description="HTTP API for Portier. Use this to interact with the Portier backend.",
|
||||||
|
csrf=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
api.add_router("/", core_router, auth=django_auth, tags=["Core"])
|
||||||
|
api.add_router("/config/", config_router, auth=django_auth, tags=["Configuration"])
|
||||||
|
api.add_router("/concierge/", concierge_router, auth=None, tags=["Concierge"])
|
|
@ -41,7 +41,8 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'rest_framework',
|
'ninja',
|
||||||
|
#'rest_framework',
|
||||||
'guardian',
|
'guardian',
|
||||||
'django_registration',
|
'django_registration',
|
||||||
'bootstrap4',
|
'bootstrap4',
|
||||||
|
@ -194,7 +195,7 @@ DJANGO_CELERY_BEAT_TZ_AWARE = False
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||||
|
|
||||||
if DEBUG:
|
if DEBUG == "LOLOLOL":
|
||||||
LOGGING = {
|
LOGGING = {
|
||||||
'version': 1,
|
'version': 1,
|
||||||
'disable_existing_loggers': False,
|
'disable_existing_loggers': False,
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import include, path
|
from django.urls import include, path
|
||||||
|
|
||||||
|
from .api import api
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('accounts/', include('django_registration.backends.activation.urls')),
|
path('accounts/', include('django_registration.backends.activation.urls')),
|
||||||
path('accounts/', include('django.contrib.auth.urls')),
|
path('accounts/', include('django.contrib.auth.urls')),
|
||||||
path('i18n/', include('django.conf.urls.i18n')),
|
path('i18n/', include('django.conf.urls.i18n')),
|
||||||
path('admin/', admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
path('config/', include('config.urls')),
|
path('config/', include('config.urls')),
|
||||||
path('concierge/', include('concierge.urls')),
|
path('api/v2/', api.urls),
|
||||||
path('api/v1/', include('restapi.urls')),
|
|
||||||
path('', include('core.urls')),
|
path('', include('core.urls')),
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,8 +5,7 @@ django-guardian
|
||||||
django-fontawesome-5
|
django-fontawesome-5
|
||||||
django-celery-beat
|
django-celery-beat
|
||||||
django-filter
|
django-filter
|
||||||
djangorestframework
|
django-ninja
|
||||||
djangorestframework-guardian
|
|
||||||
celery>=5.3
|
celery>=5.3
|
||||||
gunicorn>=20
|
gunicorn>=20
|
||||||
psycopg2-binary
|
psycopg2-binary
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
|
|
||||||
class RestapiConfig(AppConfig):
|
|
||||||
name = 'restapi'
|
|
|
@ -1,16 +0,0 @@
|
||||||
from django.urls import path, include
|
|
||||||
from rest_framework import routers
|
|
||||||
|
|
||||||
from .views import StreamViewSet, RestreamViewSet
|
|
||||||
|
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
|
||||||
router.register(r'stream', StreamViewSet)
|
|
||||||
router.register(r'restream', RestreamViewSet)
|
|
||||||
|
|
||||||
app_name = 'restapi'
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
path('', include(router.urls)),
|
|
||||||
path('auth/', include('rest_framework.urls', namespace='rest_framework'))
|
|
||||||
]
|
|
|
@ -1,51 +0,0 @@
|
||||||
from rest_framework_guardian.serializers import ObjectPermissionsAssignmentMixin
|
|
||||||
from rest_framework import serializers, viewsets
|
|
||||||
from rest_framework_guardian import filters
|
|
||||||
from config.models import Stream, Restream
|
|
||||||
|
|
||||||
|
|
||||||
class StreamSerializer(ObjectPermissionsAssignmentMixin, serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Stream
|
|
||||||
fields = '__all__'
|
|
||||||
read_only_fields = ['publish_counter']
|
|
||||||
|
|
||||||
def get_permissions_map(self, created):
|
|
||||||
current_user = self.context['request'].user
|
|
||||||
return {
|
|
||||||
'view_stream': [current_user],
|
|
||||||
'change_stream': [current_user],
|
|
||||||
'delete_stream': [current_user]
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class StreamViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Stream.objects.all()
|
|
||||||
serializer_class = StreamSerializer
|
|
||||||
filter_backends = [filters.ObjectPermissionsFilter]
|
|
||||||
|
|
||||||
|
|
||||||
class RestreamSerializer(ObjectPermissionsAssignmentMixin, serializers.ModelSerializer):
|
|
||||||
class Meta:
|
|
||||||
model = Restream
|
|
||||||
fields = '__all__'
|
|
||||||
|
|
||||||
def get_permissions_map(self, created):
|
|
||||||
current_user = self.context['request'].user
|
|
||||||
return {
|
|
||||||
'view_restream': [current_user],
|
|
||||||
'change_restream': [current_user],
|
|
||||||
'delete_restream': [current_user]
|
|
||||||
}
|
|
||||||
|
|
||||||
def validate_stream(self, value):
|
|
||||||
request = self.context['request']
|
|
||||||
if not request.user.has_perm('config.view_stream', value):
|
|
||||||
raise serializers.ValidationError('Access to stream is not authorized')
|
|
||||||
return value
|
|
||||||
|
|
||||||
|
|
||||||
class RestreamViewSet(viewsets.ModelViewSet):
|
|
||||||
queryset = Restream.objects.all()
|
|
||||||
serializer_class = RestreamSerializer
|
|
||||||
filter_backends = [filters.ObjectPermissionsFilter]
|
|
|
@ -13,7 +13,7 @@ var app = new Vue({
|
||||||
},
|
},
|
||||||
toggleActive(cfg) {
|
toggleActive(cfg) {
|
||||||
axios
|
axios
|
||||||
.patch('/api/v1/restream/' + cfg.id + '/', { active: !cfg.active })
|
.patch('/api/v2/config/restreams/' + cfg.id, { active: !cfg.active })
|
||||||
.then(response => {
|
.then(response => {
|
||||||
i = this.cfgs.findIndex((obj => obj.id == cfg.id))
|
i = this.cfgs.findIndex((obj => obj.id == cfg.id))
|
||||||
Vue.set(this.cfgs, i, response.data)
|
Vue.set(this.cfgs, i, response.data)
|
||||||
|
@ -22,7 +22,7 @@ var app = new Vue({
|
||||||
},
|
},
|
||||||
fetchData() {
|
fetchData() {
|
||||||
axios
|
axios
|
||||||
.get('/api/v1/restream/')
|
.get('/api/v2/config/restreams')
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.cfgs = response.data
|
this.cfgs = response.data
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
|
|
|
@ -16,7 +16,7 @@ var app = new Vue({
|
||||||
},
|
},
|
||||||
fetchData() {
|
fetchData() {
|
||||||
axios
|
axios
|
||||||
.get('/api/v1/stream/')
|
.get('/api/v2/config/streams')
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.streams = response.data
|
this.streams = response.data
|
||||||
this.isLoading = false
|
this.isLoading = false
|
||||||
|
|
Loading…
Reference in New Issue