Add geolocation and variable pruning

This commit is contained in:
Corey 2024-04-22 23:02:51 +00:00
parent 34b498cbfc
commit b1387b79e2
13 changed files with 176 additions and 72 deletions

17
.gitattributes vendored
View File

@ -8,13 +8,11 @@
#
# Note that binary is a macro for -text -diff.
######################################################################
# Auto detect
## Handle line endings automatically for files detected as
## text and leave all files detected as binary untouched.
## This will handle all files NOT defined below.
* text=auto
# Source code
*.bash text eol=lf
*.bat text eol=crlf
@ -52,10 +50,8 @@
*.tsx text
*.xml text
*.xhtml text diff=html
# Docker
Dockerfile text
# Documentation
*.ipynb text eol=lf
*.markdown text diff=markdown
@ -81,7 +77,6 @@ NEWS text
readme text
*README* text
TODO text
# Templates
*.dot text
*.ejs text
@ -100,7 +95,6 @@ TODO text
*.tpl text
*.twig text
*.vue text
# Configs
*.cnf text
*.conf text
@ -124,10 +118,8 @@ Makefile text
makefile text
# Fixes syntax highlighting on GitHub to allow comments
tsconfig.json linguist-language=JSON-with-Comments
# Heroku
Procfile text
# Graphics
*.ai binary
*.bmp binary
@ -155,7 +147,6 @@ Procfile text
*.tiff binary
*.wbmp binary
*.webp binary
# Audio
*.kar binary
*.m4a binary
@ -164,7 +155,6 @@ Procfile text
*.mp3 binary
*.ogg binary
*.ra binary
# Video
*.3gpp binary
*.3gp binary
@ -184,7 +174,6 @@ Procfile text
*.swc binary
*.swf binary
*.webm binary
# Archives
*.7z binary
*.gz binary
@ -192,26 +181,22 @@ Procfile text
*.rar binary
*.tar binary
*.zip binary
# Fonts
*.ttf binary
*.eot binary
*.otf binary
*.woff binary
*.woff2 binary
# Executables
*.exe binary
*.pyc binary
# Prevents massive diffs caused by vendored, minified files
**/.yarn/releases/** binary
**/.yarn/plugins/** binary
# RC files (like .babelrc or .eslintrc)
*.*rc text
# Ignore files (like .npmignore or .gitignore)
*.*ignore text
# Prevents massive diffs from built files
dist/* binary
*.mmdb filter=lfs diff=lfs merge=lfs -text

View File

@ -6,6 +6,8 @@ Ixion is designed to catalog public [LandSandBoat](https://github.com/LandSandBo
The React client fetches server data from the API and displays it in a relevant way to users. Filtering is built into the client for now. When the API response fails or is empty, a default LSB demo server is displayed, pulling some info from GitHub.
[IP Geolocation by DB-IP](https://db-ip.com)
## Features
- **Automatic Verification**: Verifies LSB server URL by sending a request to the public API. [Requires the LSB server to enable the HTTP server.](https://github.com/LandSandBoat/server/blob/df311283c4abb779d212e2b8af6734b0d8d11ad7/settings/default/network.lua#L33)

View File

@ -5,4 +5,4 @@ CSRF_TRUSTED_ORIGINS=https://api.ixion.dev
CORS_ALLOWED_ORIGINS=https://ixion.dev
# API configuration
SERVER_INACTIVITY_TIMEOUT=24
SERVER_UPDATE_INTERVAL=10

View File

@ -26,7 +26,9 @@ app.conf.result_backend = f"redis://redis:6379"
app.conf.beat_schedule = {
"verify-urls-every-hour": {
"task": "api.tasks.verify_urls_task",
"schedule": timedelta(hours=1),
"schedule": timedelta(
minutes=int(os.getenv("SERVER_UPDATE_INTERVAL", default="10"))
),
# "schedule": timedelta(minutes=1),
},
}

View File

@ -42,6 +42,7 @@ INSTALLED_APPS = [
"django.contrib.staticfiles",
"corsheaders",
"rest_framework",
"geoip2",
"v1",
]
@ -139,3 +140,5 @@ CORS_ALLOWED_ORIGINS = os.getenv(
CORS_ALLOW_ALL_ORIGINS = (
os.getenv("CORS_ALLOW_ALL_ORIGINS", default="False").lower() == "true"
)
GEOIP_PATH = os.path.join(BASE_DIR, "geoip")

View File

@ -6,3 +6,4 @@ celery
redis
python-dotenv
gunicorn
geoip2

View File

@ -0,0 +1,18 @@
# Generated by Django 5.0.4 on 2024-04-22 19:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('v1', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='server',
name='location',
field=models.CharField(editable=False, max_length=255, null=True),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 5.0.4 on 2024-04-22 20:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('v1', '0002_server_location'),
]
operations = [
migrations.RemoveField(
model_name='server',
name='inactivity_counter',
),
migrations.AddField(
model_name='server',
name='up',
field=models.BooleanField(editable=False, null=True),
),
migrations.AlterField(
model_name='server',
name='updated',
field=models.DateTimeField(editable=False, null=True),
),
]

View File

@ -1,3 +1,4 @@
import datetime
import json
import os
import socket
@ -7,6 +8,7 @@ from django.core.validators import URLValidator
from django.core.exceptions import ValidationError
from urllib.parse import urlparse
from v1.common.logging import logger
from django.contrib.gis.geoip2 import GeoIP2
# Required and recommended settings used to build the initial client server card
required_settings = [
@ -41,14 +43,20 @@ class Server(models.Model):
null=False,
validators=[OptionalSchemeURLValidator()],
)
location = models.CharField(max_length=255, null=True, editable=False)
max_level = models.IntegerField(null=True, editable=False)
settings = models.JSONField(null=True, editable=False)
customizations = models.JSONField(null=True, editable=False)
login_limit = models.IntegerField(null=True, editable=False)
active_sessions = models.IntegerField(null=True, editable=False)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
inactivity_counter = models.IntegerField(null=True, editable=False)
updated = models.DateTimeField(null=True, editable=False)
up = models.BooleanField(null=True, editable=False)
@property
def expires(self):
time_since_creation = self.updated - self.created
return self.updated + min(time_since_creation, datetime.timedelta(hours=24))
def _str_(self):
return self.url
@ -69,17 +77,14 @@ class Server(models.Model):
# Validate the server API
if not self.parse_server_api():
if (
self.inactivity_counter is None
or self.inactivity_counter
>= int(os.getenv("SERVER_INACTIVITY_TIMEOUT", default="24")) - 1
):
if self.expires <= datetime.datetime.now(datetime.timezone.utc):
return False
self.inactivity_counter += 1
self.up = False
else:
if self.settings["API.DO_NOT_TRACK"]:
return False
self.inactivity_counter = 0
self.up = True
self.updated = datetime.datetime.now(datetime.timezone.utc)
# Proceed with saving the object
logger.info(f"Saving Server object with URL: {self.url}")
@ -142,6 +147,10 @@ class Server(models.Model):
# except socket.error:
# return False
g = GeoIP2()
city = g.city(f"{self.url}")
self.location = city["continent_code"]
return True
except requests.RequestException:

View File

@ -10,12 +10,13 @@ class ServerSerializer(serializers.ModelSerializer):
"id",
"name",
"url",
"location",
"max_level",
"customizations",
"login_limit",
"active_sessions",
"updated",
"inactivity_counter",
"up",
]
def create(self, validated_data):

View File

@ -142,32 +142,57 @@ export default function ServerCard({ server }: { server: ServerData }) {
<Card className="mb-2">
<Accordion className="my-0">
<AccordionSummary expandIcon={<ExpandMore />}>
<Box className="flex items-center justify-center">
{server.customizations['LOGIN.MAINT_MODE'] === 1 ? (
<Box className="flex flex-col items-center justify-center">
<Box className="flex content-center">
{server.customizations['LOGIN.MAINT_MODE'] === 1 ? (
<Tooltip
arrow
disableInteractive
title="Server is undergoing maintenance."
>
<Warning color="warning" />
</Tooltip>
) : (
<div>
{!server.up ? (
<Tooltip
arrow
disableInteractive
title="Server is offline."
>
<PublicOff color="error" />
</Tooltip>
) : (
<Tooltip arrow disableInteractive title="Server is online.">
<Public color="success" />
</Tooltip>
)}
</div>
)}
</Box>
<Box className="flex content-center">
<Tooltip
arrow
disableInteractive
title="Server is undergoing maintenance."
title="Estimated server geolocation provided by DB-IP."
>
<Warning color="warning" />
<Typography
variant="caption"
component={Link}
to="https://db-ip.com"
target="_blank"
onClick={(event) => {
event.stopPropagation();
}}
sx={{
textDecoration: 'none',
}}
color={(theme) => alpha(theme.palette.text.primary, 0.5)}
>
{server.location}
</Typography>
</Tooltip>
) : (
<div>
{server.inactivity_counter > 0 ? (
<Tooltip
arrow
disableInteractive
title={`Offline for ${server.inactivity_counter} hours.`}
>
<PublicOff color="error" />
</Tooltip>
) : (
<Tooltip arrow disableInteractive title="Server is online.">
<Public color="success" />
</Tooltip>
)}
</div>
)}
</Box>
</Box>
<CardContent className="grow py-1">
<Box className="flex content-center">

View File

@ -15,19 +15,21 @@ export default interface ServerData {
id: number;
name: string;
url: string;
location: string;
max_level: number;
settings?: ServerSettings;
customizations: ServerSettings;
login_limit: number;
active_sessions: number;
updated: string;
inactivity_counter: number;
up: boolean;
}
export const DemoServerData: ServerData = {
id: 0,
name: 'LandSandBoat Demo',
url: 'github.com/LandSandBoat/server',
location: 'NA',
max_level: 99,
settings: LsbDefaults,
customizations: {
@ -43,7 +45,7 @@ export const DemoServerData: ServerData = {
login_limit: 0,
active_sessions: 0,
updated: new Date().toISOString(),
inactivity_counter: 0,
up: true,
};
export const ServerSettingsInfo: Record<string, ServerSetting> = {

View File

@ -108,32 +108,61 @@ export default function ServerDetails({
) : (
<Card className="mb-2">
<Box className="flex px-4">
<Box className="flex items-center justify-center">
{server.customizations['LOGIN.MAINT_MODE'] === 1 ? (
<Box className="flex flex-col items-center justify-center">
<Box className="flex content-center">
{server.customizations['LOGIN.MAINT_MODE'] === 1 ? (
<Tooltip
arrow
disableInteractive
title="Server is undergoing maintenance."
>
<Warning color="warning" />
</Tooltip>
) : (
<div>
{!server.up ? (
<Tooltip
arrow
disableInteractive
title="Server is offline."
>
<PublicOff color="error" />
</Tooltip>
) : (
<Tooltip
arrow
disableInteractive
title="Server is online."
>
<Public color="success" />
</Tooltip>
)}
</div>
)}
</Box>
<Box className="flex content-center">
<Tooltip
arrow
disableInteractive
title="Server is undergoing maintenance."
title="Estimated server geolocation provided by DB-IP"
>
<Warning color="warning" />
<Typography
variant="caption"
component={Link}
to="https://db-ip.com"
target="_blank"
onClick={(event) => {
event.stopPropagation();
}}
sx={{
textDecoration: 'none',
}}
color={(theme) => alpha(theme.palette.text.primary, 0.5)}
>
{server.location}
</Typography>
</Tooltip>
) : (
<div>
{server.inactivity_counter > 0 ? (
<Tooltip
arrow
disableInteractive
title={`Offline for ${server.inactivity_counter} hours.`}
>
<PublicOff color="error" />
</Tooltip>
) : (
<Tooltip arrow disableInteractive title="Server is online.">
<Public color="success" />
</Tooltip>
)}
</div>
)}
</Box>
</Box>
<CardContent className="grow py-1">
<Box className="flex content-center">