Compare commits

..

3 Commits
master ... api

Author SHA1 Message Date
Jukoga b0afa5c7be Rename markdown extras files and update Docker commands for API migrations 2025-12-16 17:27:05 +01:00
Surya 50aed1a48a Update requirements.txt 2025-12-16 15:00:33 +01:00
Surya 02a6904834 Added API 2025-12-16 19:25:43 +05:30
17 changed files with 336 additions and 3 deletions

View File

@ -40,12 +40,14 @@ INSTALLED_APPS = [
'crispy_forms', 'crispy_forms',
'crispy_bootstrap4', 'crispy_bootstrap4',
'blog.apps.BlogConfig', 'blog.apps.BlogConfig',
'api.apps.APIConfig',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'rest_framework',
] ]
MIDDLEWARE = [ MIDDLEWARE = [
@ -112,6 +114,13 @@ AUTH_PASSWORD_VALIDATORS = [
}, },
] ]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/ # https://docs.djangoproject.com/en/3.0/topics/i18n/
@ -138,3 +147,8 @@ MEDIA_URL = '/media/'
CRISPY_TEMPLATE_PACK = 'bootstrap4' CRISPY_TEMPLATE_PACK = 'bootstrap4'
LOGIN_REDIRECT_URL = 'News' LOGIN_REDIRECT_URL = 'News'
LOGIN_URL = 'Login' LOGIN_URL = 'Login'
# Default primary key field type
# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

View File

@ -20,6 +20,7 @@ from django.urls import path, include
urlpatterns = [ urlpatterns = [
path('', include('blog.urls')), path('', include('blog.urls')),
path('api/', include('api.urls')),
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
] ]

0
api/__init__.py Normal file
View File

9
api/admin.py Normal file
View File

@ -0,0 +1,9 @@
from django.contrib import admin
from .models import *
# Register your models here.
admin.site.register(Province)
admin.site.register(AssaultTroop)
admin.site.register(Player)
admin.site.register(Server)

5
api/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class APIConfig(AppConfig):
name = 'api'

70
api/models.py Normal file
View File

@ -0,0 +1,70 @@
from django.db import models
from django.contrib.auth.models import User
from django.db.models.fields import CharField, IntegerField
# Create your models here.
class Province(models.Model):
id = models.IntegerField(primary_key = True)
name = models.CharField(max_length = 100, blank = False)
faction = models.IntegerField() # 0 - Neutral, 1 - Allies, 2 - Axis
map = models.CharField(max_length=255)
mov_speed = models.IntegerField()
ats = models.JSONField(null=True)
class Meta:
ordering = ['id']
def __str__(self):
return f"{self.name} - {self.faction}"
class AssaultTroop(models.Model):
id = models.IntegerField(primary_key = True)
name = models.CharField(max_length = 100, blank = False)
faction = models.IntegerField() # 0 - Neutral, 1 - Allies, 2 - Axis
type = models.IntegerField()
province = models.IntegerField() # Province ID (-1 for not deployed)
orders = models.JSONField(null=True)
owner = models.ForeignKey(
'auth.User',
related_name='ats',
on_delete=models.CASCADE
)
class Meta:
ordering = ['id']
def __str__(self):
return f"{self.name} - {self.province},{self.faction}"
class Player(models.Model):
id = models.IntegerField(primary_key = True)
name = models.CharField(max_length = 50, blank = False)
faction = models.IntegerField()
server = CharField(max_length = 20, blank = False)
class Meta:
ordering = ['id']
def __str__(self):
return self.name
class Server(models.Model):
id = IntegerField(primary_key = True)
players = IntegerField() # total current players
capacity = IntegerField() # max player capacity
region = CharField(max_length = 3, blank = False) # 3 letter abb. for region
address = CharField(max_length = 20, blank = False)
class Meta:
ordering = ['id']
def __str__(self):
return f"{self.address} - {self.region}"

25
api/serializers.py Normal file
View File

@ -0,0 +1,25 @@
from rest_framework import serializers
from .models import *
class ProvinceSerializer(serializers.ModelSerializer):
class Meta:
model = Province
fields = ['id', 'name', 'faction', 'map', 'mov_speed', 'ats']
class AssaultTroopSerializer(serializers.ModelSerializer):
class Meta:
model = AssaultTroop
fields = ['id', 'name', 'faction', 'type', 'province', 'deployed', 'orders', 'owner']
class PlayerSerializer(serializers.ModelSerializer):
class Meta:
model = Player
fields = ['id', 'name', 'faction', 'server']
class ServerSerializer(serializers.ModelSerializer):
class Meta:
model = Server
fields = ['id', 'players', 'capacity', 'region', 'address']

3
api/tests.py Normal file
View File

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

13
api/urls.py Normal file
View File

@ -0,0 +1,13 @@
from django.urls import path
from .views import *
urlpatterns = [
path('provinces/', ProvincesView),
path('provinces/<int:nm>/', ProvinceView),
path('assault_troops/', AssaultTroopsView),
path('assault_troops/<int:nm>/', AssaultTroopView),
path('players/', PlayersView),
path('players/<int:nm>/', PlayerView),
path('servers/', ServersView),
path('servers/<int:nm>/', ServerView),
]

180
api/views.py Normal file
View File

@ -0,0 +1,180 @@
from django.shortcuts import render, HttpResponseRedirect, Http404
from rest_framework.parsers import JSONParser
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from .models import *
from .serializers import *
# Create your views here.
@csrf_exempt
def ProvincesView(request):
if request.method == 'GET':
items = Province.objects.all()
serializer = ProvinceSerializer(items, many = True)
return JsonResponse(serializer.data, safe = False)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = ProvinceSerializer(data = data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data,status = 201)
return JsonResponse(serializer.errors,status = 400)
@csrf_exempt
def AssaultTroopsView(request):
if request.method == 'GET':
items = AssaultTroop.objects.all()
serializer = AssaultTroopSerializer(items, many = True)
return JsonResponse(serializer.data, safe = False)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = AssaultTroopSerializer(data = data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data,status = 201)
return JsonResponse(serializer.errors,status = 400)
@csrf_exempt
def ProvinceView(request, nm):
try:
item = Province.objects.get(id = nm)
except Province.DoesNotExist:
raise Http404('Not found')
if request.method == 'GET':
serializer = ProvinceSerializer(item)
return JsonResponse(serializer.data)
if request.method == 'PUT':
data = JSONParser().parse(request)
serializer = ProvinceSerializer(item,data =data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status =400)
if request.method == "DELETE":
item.delete()
return HttpResponse(status =204)
@csrf_exempt
def AssaultTroopView(request, nm):
try:
item = AssaultTroop.objects.get(id = nm)
except AssaultTroop.DoesNotExist:
raise Http404('Not found')
if request.method == 'GET':
serializer = AssaultTroopSerializer(item)
return JsonResponse(serializer.data)
if request.method == 'PUT':
data = JSONParser().parse(request)
serializer = AssaultTroopSerializer(item,data =data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status =400)
if request.method == "DELETE":
item.delete()
return HttpResponse(status =204)
@csrf_exempt
def PlayersView(request):
if request.method == 'GET':
items = Player.objects.all()
serializer = PlayerSerializer(items, many = True)
return JsonResponse(serializer.data, safe = False)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = PlayerSerializer(data = data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data,status = 201)
return JsonResponse(serializer.errors,status = 400)
@csrf_exempt
def ServersView(request):
if request.method == 'GET':
items = Server.objects.all()
serializer = ServerSerializer(items, many = True)
return JsonResponse(serializer.data, safe = False)
elif request.method == 'POST':
data = JSONParser().parse(request)
serializer = ServerSerializer(data = data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data,status = 201)
return JsonResponse(serializer.errors,status = 400)
@csrf_exempt
def PlayerView(request, nm):
try:
item = Player.objects.get(id = nm)
except Player.DoesNotExist:
raise Http404('Not found')
if request.method == 'GET':
serializer = PlayerSerializer(item)
return JsonResponse(serializer.data)
if request.method == 'PUT':
data = JSONParser().parse(request)
serializer = PlayerSerializer(item,data =data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status =400)
if request.method == "DELETE":
item.delete()
return HttpResponse(status =204)
@csrf_exempt
def ServerView(request, nm):
try:
item = Server.objects.get(id = nm)
except Server.DoesNotExist:
raise Http404('Not found')
if request.method == 'GET':
serializer = ServerSerializer(item)
return JsonResponse(serializer.data)
if request.method == 'PUT':
data = JSONParser().parse(request)
serializer = ServerSerializer(item,data =data)
if serializer.is_valid():
serializer.save()
return JsonResponse(serializer.data)
return JsonResponse(serializer.errors, status =400)
if request.method == "DELETE":
item.delete()
return HttpResponse(status =204)

View File

@ -0,0 +1,12 @@
from django import template
from django.template.defaultfilters import stringfilter
import markdown as md
register = template.Library()
@register.filter()
@stringfilter
def markdown(value):
return md.markdown(value, extensions=['markdown.extensions.fenced_code'])

View File

@ -10,6 +10,7 @@ services:
network_mode: "host" network_mode: "host"
command: > command: >
sh -c "python manage.py makemigrations blog --noinput && sh -c "python manage.py makemigrations blog --noinput &&
python manage.py makemigrations api --noinput &&
python manage.py migrate --noinput && python manage.py migrate --noinput &&
python manage.py collectstatic --noinput && python manage.py collectstatic --noinput &&
gunicorn BH.wsgi:application --bind 0.0.0.0:3030 --workers ${GUNICORN_WORKERS:-3}" gunicorn BH.wsgi:application --bind 0.0.0.0:3030 --workers ${GUNICORN_WORKERS:-3}"

Binary file not shown.

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
{% load markdown_extras %} {% load blog_markdown_extras %}
<div class="container mt-5"> <div class="container mt-5">
<div class="card mb-3"> <div class="card mb-3">

View File

@ -1,7 +1,7 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
{% load markdown_extras %} {% load blog_markdown_extras %}
<!-- Main Content --> <!-- Main Content -->
<div class="container mt-5"> <div class="container mt-5">
<div class="row"> <div class="row">

View File

@ -16,7 +16,7 @@
<div class="container"> <div class="container">
<h3 style="text-align: justify;">Our Developers</h3> <h3 style="text-align: justify;">Our Developers</h3>
<p style="text-align: justify;"> <p style="text-align: justify;">
Made up of enthusiastic helpers from all over the globe, with a strong core in Germany, Poland, France, India, Argentina, and more. Sharing ideas across so many cultures gives us fresh perspectives and helps us keep eyes on different parts that might be more relevant locally and their experience with other mmos. That way, we can reach farther and keep a foot in major countries around the globe. If you'd like to join, just <a href="https://forms.gle/wf4LTUHNW7hjPXcz6">fill out the form</a> and don't be scared to hit us up on Discord! Our work environment is super hostile and we do require you to have a sense of humor and the latest version of sarcasm which you can get here <a href="https://en.wikipedia.org/wiki/Sarcasm">Sarcasm-StopMakingNewBranches-win-x64.7z</a><br> Made up of enthusiastic helpers from all over the globe, with a strong core in Germany, Poland, France, India, Argentina, and more. Sharing ideas across so many cultures gives us fresh perspectives and helps us keep eyes on different parts that might be more relevant locally and their experience with other mmos. That way, we can reach farther and keep a foot in major countries around the globe. If you'd like to join, just <a href="https://forms.gle/wf4LTUHNW7hjPXcz6">fill out the form</a> and don't be scared to hit us up on Discord! Our work environment is super hostile and we do require you to have a sense of humor and the latest version of sarcasm which you can get here <a href="https://en.wikipedia.org/wiki/Sarcasm">SarcasmPgv2.06.1-HOTFIX-fixby.StopMakingNewBranchesPleaseFFS-win-x64.7z</a><br>
</p> </p>
</div> </div>
<div class="container"> <div class="container">