mirror of https://github.com/cocosolos/ixion.git
Add geolocation and variable pruning
This commit is contained in:
parent
34b498cbfc
commit
b1387b79e2
|
|
@ -8,13 +8,11 @@
|
||||||
#
|
#
|
||||||
# Note that binary is a macro for -text -diff.
|
# Note that binary is a macro for -text -diff.
|
||||||
######################################################################
|
######################################################################
|
||||||
|
|
||||||
# Auto detect
|
# Auto detect
|
||||||
## Handle line endings automatically for files detected as
|
## Handle line endings automatically for files detected as
|
||||||
## text and leave all files detected as binary untouched.
|
## text and leave all files detected as binary untouched.
|
||||||
## This will handle all files NOT defined below.
|
## This will handle all files NOT defined below.
|
||||||
* text=auto
|
* text=auto
|
||||||
|
|
||||||
# Source code
|
# Source code
|
||||||
*.bash text eol=lf
|
*.bash text eol=lf
|
||||||
*.bat text eol=crlf
|
*.bat text eol=crlf
|
||||||
|
|
@ -52,10 +50,8 @@
|
||||||
*.tsx text
|
*.tsx text
|
||||||
*.xml text
|
*.xml text
|
||||||
*.xhtml text diff=html
|
*.xhtml text diff=html
|
||||||
|
|
||||||
# Docker
|
# Docker
|
||||||
Dockerfile text
|
Dockerfile text
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
*.ipynb text eol=lf
|
*.ipynb text eol=lf
|
||||||
*.markdown text diff=markdown
|
*.markdown text diff=markdown
|
||||||
|
|
@ -81,7 +77,6 @@ NEWS text
|
||||||
readme text
|
readme text
|
||||||
*README* text
|
*README* text
|
||||||
TODO text
|
TODO text
|
||||||
|
|
||||||
# Templates
|
# Templates
|
||||||
*.dot text
|
*.dot text
|
||||||
*.ejs text
|
*.ejs text
|
||||||
|
|
@ -100,7 +95,6 @@ TODO text
|
||||||
*.tpl text
|
*.tpl text
|
||||||
*.twig text
|
*.twig text
|
||||||
*.vue text
|
*.vue text
|
||||||
|
|
||||||
# Configs
|
# Configs
|
||||||
*.cnf text
|
*.cnf text
|
||||||
*.conf text
|
*.conf text
|
||||||
|
|
@ -124,10 +118,8 @@ Makefile text
|
||||||
makefile text
|
makefile text
|
||||||
# Fixes syntax highlighting on GitHub to allow comments
|
# Fixes syntax highlighting on GitHub to allow comments
|
||||||
tsconfig.json linguist-language=JSON-with-Comments
|
tsconfig.json linguist-language=JSON-with-Comments
|
||||||
|
|
||||||
# Heroku
|
# Heroku
|
||||||
Procfile text
|
Procfile text
|
||||||
|
|
||||||
# Graphics
|
# Graphics
|
||||||
*.ai binary
|
*.ai binary
|
||||||
*.bmp binary
|
*.bmp binary
|
||||||
|
|
@ -155,7 +147,6 @@ Procfile text
|
||||||
*.tiff binary
|
*.tiff binary
|
||||||
*.wbmp binary
|
*.wbmp binary
|
||||||
*.webp binary
|
*.webp binary
|
||||||
|
|
||||||
# Audio
|
# Audio
|
||||||
*.kar binary
|
*.kar binary
|
||||||
*.m4a binary
|
*.m4a binary
|
||||||
|
|
@ -164,7 +155,6 @@ Procfile text
|
||||||
*.mp3 binary
|
*.mp3 binary
|
||||||
*.ogg binary
|
*.ogg binary
|
||||||
*.ra binary
|
*.ra binary
|
||||||
|
|
||||||
# Video
|
# Video
|
||||||
*.3gpp binary
|
*.3gpp binary
|
||||||
*.3gp binary
|
*.3gp binary
|
||||||
|
|
@ -184,7 +174,6 @@ Procfile text
|
||||||
*.swc binary
|
*.swc binary
|
||||||
*.swf binary
|
*.swf binary
|
||||||
*.webm binary
|
*.webm binary
|
||||||
|
|
||||||
# Archives
|
# Archives
|
||||||
*.7z binary
|
*.7z binary
|
||||||
*.gz binary
|
*.gz binary
|
||||||
|
|
@ -192,26 +181,22 @@ Procfile text
|
||||||
*.rar binary
|
*.rar binary
|
||||||
*.tar binary
|
*.tar binary
|
||||||
*.zip binary
|
*.zip binary
|
||||||
|
|
||||||
# Fonts
|
# Fonts
|
||||||
*.ttf binary
|
*.ttf binary
|
||||||
*.eot binary
|
*.eot binary
|
||||||
*.otf binary
|
*.otf binary
|
||||||
*.woff binary
|
*.woff binary
|
||||||
*.woff2 binary
|
*.woff2 binary
|
||||||
|
|
||||||
# Executables
|
# Executables
|
||||||
*.exe binary
|
*.exe binary
|
||||||
*.pyc binary
|
*.pyc binary
|
||||||
# Prevents massive diffs caused by vendored, minified files
|
# Prevents massive diffs caused by vendored, minified files
|
||||||
**/.yarn/releases/** binary
|
**/.yarn/releases/** binary
|
||||||
**/.yarn/plugins/** binary
|
**/.yarn/plugins/** binary
|
||||||
|
|
||||||
# RC files (like .babelrc or .eslintrc)
|
# RC files (like .babelrc or .eslintrc)
|
||||||
*.*rc text
|
*.*rc text
|
||||||
|
|
||||||
# Ignore files (like .npmignore or .gitignore)
|
# Ignore files (like .npmignore or .gitignore)
|
||||||
*.*ignore text
|
*.*ignore text
|
||||||
|
|
||||||
# Prevents massive diffs from built files
|
# Prevents massive diffs from built files
|
||||||
dist/* binary
|
dist/* binary
|
||||||
|
*.mmdb filter=lfs diff=lfs merge=lfs -text
|
||||||
|
|
|
||||||
|
|
@ -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.
|
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
|
## 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)
|
- **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)
|
||||||
|
|
|
||||||
|
|
@ -5,4 +5,4 @@ CSRF_TRUSTED_ORIGINS=https://api.ixion.dev
|
||||||
CORS_ALLOWED_ORIGINS=https://ixion.dev
|
CORS_ALLOWED_ORIGINS=https://ixion.dev
|
||||||
|
|
||||||
# API configuration
|
# API configuration
|
||||||
SERVER_INACTIVITY_TIMEOUT=24
|
SERVER_UPDATE_INTERVAL=10
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ app.conf.result_backend = f"redis://redis:6379"
|
||||||
app.conf.beat_schedule = {
|
app.conf.beat_schedule = {
|
||||||
"verify-urls-every-hour": {
|
"verify-urls-every-hour": {
|
||||||
"task": "api.tasks.verify_urls_task",
|
"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),
|
# "schedule": timedelta(minutes=1),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ INSTALLED_APPS = [
|
||||||
"django.contrib.staticfiles",
|
"django.contrib.staticfiles",
|
||||||
"corsheaders",
|
"corsheaders",
|
||||||
"rest_framework",
|
"rest_framework",
|
||||||
|
"geoip2",
|
||||||
"v1",
|
"v1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
@ -139,3 +140,5 @@ CORS_ALLOWED_ORIGINS = os.getenv(
|
||||||
CORS_ALLOW_ALL_ORIGINS = (
|
CORS_ALLOW_ALL_ORIGINS = (
|
||||||
os.getenv("CORS_ALLOW_ALL_ORIGINS", default="False").lower() == "true"
|
os.getenv("CORS_ALLOW_ALL_ORIGINS", default="False").lower() == "true"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
GEOIP_PATH = os.path.join(BASE_DIR, "geoip")
|
||||||
|
|
|
||||||
|
|
@ -6,3 +6,4 @@ celery
|
||||||
redis
|
redis
|
||||||
python-dotenv
|
python-dotenv
|
||||||
gunicorn
|
gunicorn
|
||||||
|
geoip2
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import datetime
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import socket
|
import socket
|
||||||
|
|
@ -7,6 +8,7 @@ from django.core.validators import URLValidator
|
||||||
from django.core.exceptions import ValidationError
|
from django.core.exceptions import ValidationError
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
from v1.common.logging import logger
|
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 and recommended settings used to build the initial client server card
|
||||||
required_settings = [
|
required_settings = [
|
||||||
|
|
@ -41,14 +43,20 @@ class Server(models.Model):
|
||||||
null=False,
|
null=False,
|
||||||
validators=[OptionalSchemeURLValidator()],
|
validators=[OptionalSchemeURLValidator()],
|
||||||
)
|
)
|
||||||
|
location = models.CharField(max_length=255, null=True, editable=False)
|
||||||
max_level = models.IntegerField(null=True, editable=False)
|
max_level = models.IntegerField(null=True, editable=False)
|
||||||
settings = models.JSONField(null=True, editable=False)
|
settings = models.JSONField(null=True, editable=False)
|
||||||
customizations = models.JSONField(null=True, editable=False)
|
customizations = models.JSONField(null=True, editable=False)
|
||||||
login_limit = models.IntegerField(null=True, editable=False)
|
login_limit = models.IntegerField(null=True, editable=False)
|
||||||
active_sessions = models.IntegerField(null=True, editable=False)
|
active_sessions = models.IntegerField(null=True, editable=False)
|
||||||
created = models.DateTimeField(auto_now_add=True)
|
created = models.DateTimeField(auto_now_add=True)
|
||||||
updated = models.DateTimeField(auto_now=True)
|
updated = models.DateTimeField(null=True, editable=False)
|
||||||
inactivity_counter = models.IntegerField(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):
|
def _str_(self):
|
||||||
return self.url
|
return self.url
|
||||||
|
|
@ -69,17 +77,14 @@ class Server(models.Model):
|
||||||
|
|
||||||
# Validate the server API
|
# Validate the server API
|
||||||
if not self.parse_server_api():
|
if not self.parse_server_api():
|
||||||
if (
|
if self.expires <= datetime.datetime.now(datetime.timezone.utc):
|
||||||
self.inactivity_counter is None
|
|
||||||
or self.inactivity_counter
|
|
||||||
>= int(os.getenv("SERVER_INACTIVITY_TIMEOUT", default="24")) - 1
|
|
||||||
):
|
|
||||||
return False
|
return False
|
||||||
self.inactivity_counter += 1
|
self.up = False
|
||||||
else:
|
else:
|
||||||
if self.settings["API.DO_NOT_TRACK"]:
|
if self.settings["API.DO_NOT_TRACK"]:
|
||||||
return False
|
return False
|
||||||
self.inactivity_counter = 0
|
self.up = True
|
||||||
|
self.updated = datetime.datetime.now(datetime.timezone.utc)
|
||||||
|
|
||||||
# Proceed with saving the object
|
# Proceed with saving the object
|
||||||
logger.info(f"Saving Server object with URL: {self.url}")
|
logger.info(f"Saving Server object with URL: {self.url}")
|
||||||
|
|
@ -142,6 +147,10 @@ class Server(models.Model):
|
||||||
# except socket.error:
|
# except socket.error:
|
||||||
# return False
|
# return False
|
||||||
|
|
||||||
|
g = GeoIP2()
|
||||||
|
city = g.city(f"{self.url}")
|
||||||
|
self.location = city["continent_code"]
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
except requests.RequestException:
|
except requests.RequestException:
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,13 @@ class ServerSerializer(serializers.ModelSerializer):
|
||||||
"id",
|
"id",
|
||||||
"name",
|
"name",
|
||||||
"url",
|
"url",
|
||||||
|
"location",
|
||||||
"max_level",
|
"max_level",
|
||||||
"customizations",
|
"customizations",
|
||||||
"login_limit",
|
"login_limit",
|
||||||
"active_sessions",
|
"active_sessions",
|
||||||
"updated",
|
"updated",
|
||||||
"inactivity_counter",
|
"up",
|
||||||
]
|
]
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,8 @@ export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
<Card className="mb-2">
|
<Card className="mb-2">
|
||||||
<Accordion className="my-0">
|
<Accordion className="my-0">
|
||||||
<AccordionSummary expandIcon={<ExpandMore />}>
|
<AccordionSummary expandIcon={<ExpandMore />}>
|
||||||
<Box className="flex items-center justify-center">
|
<Box className="flex flex-col items-center justify-center">
|
||||||
|
<Box className="flex content-center">
|
||||||
{server.customizations['LOGIN.MAINT_MODE'] === 1 ? (
|
{server.customizations['LOGIN.MAINT_MODE'] === 1 ? (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
arrow
|
arrow
|
||||||
|
|
@ -153,11 +154,11 @@ export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
{server.inactivity_counter > 0 ? (
|
{!server.up ? (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
arrow
|
arrow
|
||||||
disableInteractive
|
disableInteractive
|
||||||
title={`Offline for ${server.inactivity_counter} hours.`}
|
title="Server is offline."
|
||||||
>
|
>
|
||||||
<PublicOff color="error" />
|
<PublicOff color="error" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
@ -169,6 +170,30 @@ export default function ServerCard({ server }: { server: ServerData }) {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
<Box className="flex content-center">
|
||||||
|
<Tooltip
|
||||||
|
arrow
|
||||||
|
disableInteractive
|
||||||
|
title="Estimated server geolocation provided by DB-IP."
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
<CardContent className="grow py-1">
|
<CardContent className="grow py-1">
|
||||||
<Box className="flex content-center">
|
<Box className="flex content-center">
|
||||||
<Typography
|
<Typography
|
||||||
|
|
|
||||||
|
|
@ -15,19 +15,21 @@ export default interface ServerData {
|
||||||
id: number;
|
id: number;
|
||||||
name: string;
|
name: string;
|
||||||
url: string;
|
url: string;
|
||||||
|
location: string;
|
||||||
max_level: number;
|
max_level: number;
|
||||||
settings?: ServerSettings;
|
settings?: ServerSettings;
|
||||||
customizations: ServerSettings;
|
customizations: ServerSettings;
|
||||||
login_limit: number;
|
login_limit: number;
|
||||||
active_sessions: number;
|
active_sessions: number;
|
||||||
updated: string;
|
updated: string;
|
||||||
inactivity_counter: number;
|
up: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DemoServerData: ServerData = {
|
export const DemoServerData: ServerData = {
|
||||||
id: 0,
|
id: 0,
|
||||||
name: 'LandSandBoat Demo',
|
name: 'LandSandBoat Demo',
|
||||||
url: 'github.com/LandSandBoat/server',
|
url: 'github.com/LandSandBoat/server',
|
||||||
|
location: 'NA',
|
||||||
max_level: 99,
|
max_level: 99,
|
||||||
settings: LsbDefaults,
|
settings: LsbDefaults,
|
||||||
customizations: {
|
customizations: {
|
||||||
|
|
@ -43,7 +45,7 @@ export const DemoServerData: ServerData = {
|
||||||
login_limit: 0,
|
login_limit: 0,
|
||||||
active_sessions: 0,
|
active_sessions: 0,
|
||||||
updated: new Date().toISOString(),
|
updated: new Date().toISOString(),
|
||||||
inactivity_counter: 0,
|
up: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ServerSettingsInfo: Record<string, ServerSetting> = {
|
export const ServerSettingsInfo: Record<string, ServerSetting> = {
|
||||||
|
|
|
||||||
|
|
@ -108,7 +108,8 @@ export default function ServerDetails({
|
||||||
) : (
|
) : (
|
||||||
<Card className="mb-2">
|
<Card className="mb-2">
|
||||||
<Box className="flex px-4">
|
<Box className="flex px-4">
|
||||||
<Box className="flex items-center justify-center">
|
<Box className="flex flex-col items-center justify-center">
|
||||||
|
<Box className="flex content-center">
|
||||||
{server.customizations['LOGIN.MAINT_MODE'] === 1 ? (
|
{server.customizations['LOGIN.MAINT_MODE'] === 1 ? (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
arrow
|
arrow
|
||||||
|
|
@ -119,22 +120,50 @@ export default function ServerDetails({
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<div>
|
<div>
|
||||||
{server.inactivity_counter > 0 ? (
|
{!server.up ? (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
arrow
|
arrow
|
||||||
disableInteractive
|
disableInteractive
|
||||||
title={`Offline for ${server.inactivity_counter} hours.`}
|
title="Server is offline."
|
||||||
>
|
>
|
||||||
<PublicOff color="error" />
|
<PublicOff color="error" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
) : (
|
) : (
|
||||||
<Tooltip arrow disableInteractive title="Server is online.">
|
<Tooltip
|
||||||
|
arrow
|
||||||
|
disableInteractive
|
||||||
|
title="Server is online."
|
||||||
|
>
|
||||||
<Public color="success" />
|
<Public color="success" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
<Box className="flex content-center">
|
||||||
|
<Tooltip
|
||||||
|
arrow
|
||||||
|
disableInteractive
|
||||||
|
title="Estimated server geolocation provided by DB-IP"
|
||||||
|
>
|
||||||
|
<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>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
<CardContent className="grow py-1">
|
<CardContent className="grow py-1">
|
||||||
<Box className="flex content-center">
|
<Box className="flex content-center">
|
||||||
<Typography
|
<Typography
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue