This commit is contained in:
GamerNoTitle 2024-09-22 18:14:36 +08:00
commit 81ddc973d9
8 changed files with 312 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
venv
__pycache__

68
README.md Normal file
View File

@ -0,0 +1,68 @@
# JetbrainsLSAvaliableChecker
Check a Jetbrains License Server is avaliable or not.
## Quick Start
- Install Python
- ```bash
$ pip install -r requirements.txt
```
- ```bash
$ uvicorn app.main:app --reload
```
## API Usage
- Path: `/api/fetch`
- Methods: `GET` or `POST`
- params:
- `server`: The server url of the license server
- Return:
- ```json
{
"available": bool,
"message": str,
"data": {
"release_ticket_data": {
"action": str,
"confirmation_stamp": str,
"lease_signature": str,
"response_code": str,
"salt": str,
"server_lease": str,
"server_uid": str,
"validation_deadline_period": str,
"validation_period": str
},
"obtain_ticket_data": {
"action": str,
"confirmation_stamp": str,
"lease_signature": str,
"message": str,
"prolongation_period": str,
"response_code": str,
"salt": str,
"server_lease": str,
"server_uid": str,
"validation_deadline_period": str,
"validation_period": str
}
}
}
```
## Disclaimer
This program and its related documentation are provided for general informational purposes only. While we strive to ensure the accuracy and completeness of the information, we make no representations or warranties of any kind, express or implied, regarding the accuracy, reliability, or completeness of the content. You use this program at your own risk.
We shall not be liable for any direct, indirect, incidental, special, or consequential damages arising from the use or inability to use this program, including but not limited to loss of data or profits, even if we have been advised of the possibility of such damages.
The use of this program may be subject to certain legal and regulatory requirements, and users are responsible for understanding and complying with applicable laws and regulations. We reserve the right to modify this disclaimer at any time without notice.

0
app/__init__.py Normal file
View File

94
app/main.py Normal file
View File

@ -0,0 +1,94 @@
from fastapi import FastAPI, Form, Request
from fastapi.responses import HTMLResponse, JSONResponse
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
import httpx
import uuid
import string
import random
import xml.etree.ElementTree as ET
app = FastAPI()
templates = Jinja2Templates(directory="app/templates")
ascii_list = list(string.ascii_letters + string.digits)
DESKTOP_NAME = "DESKTOP-" + "".join(random.sample(ascii_list, 7)).upper()
MACHINE_ID = uuid.uuid4()
USERNAME = "".join(random.sample(ascii_list, 7)).upper()
VERSION = 2024100
BUILD_NUMBER = "2024.1.4+Build+CL-241.18034.45"
# 配置静态文件
app.mount("/static", StaticFiles(directory="app/static"), name="static")
# 获取并解析 XML 响应
def get_xml_data(server: str, ticket_path: str):
client = httpx.Client(verify=False)
try:
response = client.get(server + ticket_path, timeout=3) # 设置超时为10秒
response.raise_for_status() # 检查响应状态
root = ET.fromstring(response.text)
return {
"action": root.find("action").text,
"confirmation_stamp": root.find("confirmationStamp").text,
"lease_signature": root.find("leaseSignature").text,
"response_code": root.find("responseCode").text,
"salt": root.find("salt").text,
"server_lease": root.find("serverLease").text,
"server_uid": root.find("serverUid").text,
"validation_deadline_period": root.find("validationDeadlinePeriod").text,
"validation_period": root.find("validationPeriod").text,
"message": root.find("message").text
}
except httpx.ReadTimeout:
return {"error": "Timeout: Please try again later."}
except Exception as e:
return {"error": f"An unexpected error occurred: {str(e)}. It seems that this server is not avaliable or you can try again later."}
@app.get("/", response_class=HTMLResponse)
async def index(request: Request):
return templates.TemplateResponse("index.html", {"request": request})
@app.post("/fetch", response_class=HTMLResponse)
async def fetch_data(request: Request, server: str = Form(...)):
RELEASE_TICKET_PATH = f"/rpc/releaseTicket.action?buildNumber={BUILD_NUMBER}&clientVersion=16&hostName={DESKTOP_NAME}&machineId={MACHINE_ID}&productCode=cfc7082d-ae43-4978-a2a2-46feb1679405&productFamilyId=cfc7082d-ae43-4978-a2a2-46feb1679405&salt=1726973268812&secure=false&ticketId=rsoaehfbbg&userName={USERNAME}"
OBTAIN_TICKET_PATH = f"/rpc/obtainTicket.action?buildDate=20240409&{BUILD_NUMBER}&clientVersion=16&hostName={DESKTOP_NAME}&machineId={MACHINE_ID}&productCode=cfc7082d-ae43-4978-a2a2-46feb1679405&productFamilyId=cfc7082d-ae43-4978-a2a2-46feb1679405&salt=1726973269845&secure=false&userName={USERNAME}&version={VERSION}&versionNumber={VERSION}"
release_ticket_data = get_xml_data(server, RELEASE_TICKET_PATH)
obtain_ticket_data = get_xml_data(server, OBTAIN_TICKET_PATH)
return templates.TemplateResponse("index.html", {
"request": request,
"release_data": release_ticket_data,
"obtain_data": obtain_ticket_data,
"server": server
})
@app.get("/api/fetch")
@app.post("/api/fetch")
async def api_fetch(server: str = Form(...)):
return await handle_fetch(server)
async def handle_fetch(server: str):
RELEASE_TICKET_PATH = f"/rpc/releaseTicket.action?buildNumber={BUILD_NUMBER}&clientVersion=16&hostName={DESKTOP_NAME}&machineId={MACHINE_ID}&productCode=cfc7082d-ae43-4978-a2a2-46feb1679405&productFamilyId=cfc7082d-ae43-4978-a2a2-46feb1679405&salt=1726973268812&secure=false&ticketId=rsoaehfbbg&userName={USERNAME}"
OBTAIN_TICKET_PATH = f"/rpc/obtainTicket.action?buildDate=20240409&{BUILD_NUMBER}&clientVersion=16&hostName={DESKTOP_NAME}&machineId={MACHINE_ID}&productCode=cfc7082d-ae43-4978-a2a2-46feb1679405&productFamilyId=cfc7082d-ae43-4978-a2a2-46feb1679405&salt=1726973269845&secure=false&userName={USERNAME}&version={VERSION}&versionNumber={VERSION}"
release_ticket_data = get_xml_data(server, RELEASE_TICKET_PATH)
obtain_ticket_data = get_xml_data(server, OBTAIN_TICKET_PATH)
available = False
if obtain_ticket_data.get("response_code") == "OK":
available = True
elif obtain_ticket_data.get("response_code") == "Error":
available = False
return JSONResponse(content={
"available": available,
"message": obtain_ticket_data.get("message", ""),
"data": {
"release_ticket_data": release_ticket_data,
"obtain_ticket_data": obtain_ticket_data,
}
})

7
app/static/scripts.js Normal file
View File

@ -0,0 +1,7 @@
document.getElementById('server-form').addEventListener('submit', function(e) {
const serverInput = document.getElementById('server').value;
if (!serverInput.startsWith('https://') && !serverInput.startsWith('http://')) {
e.preventDefault();
alert('Please enter a valid server URL starting with https:// or http://');
}
});

69
app/static/styles.css Normal file
View File

@ -0,0 +1,69 @@
body {
font-family: Arial, sans-serif;
margin: 20px;
background-color: #f4f4f4;
color: #333;
}
h1 {
color: #007bff;
text-align: center;
}
form {
margin-bottom: 20px;
text-align: center;
}
input[type="text"] {
padding: 10px;
margin-right: 10px;
width: 300px;
}
button {
padding: 10px 15px;
background-color: #007bff;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #0056b3;
}
hr {
margin: 40px 0;
}
#results {
margin-top: 20px;
}
form {
margin-bottom: 20px;
}
table {
width: 1200px;
border-collapse: collapse;
margin-bottom: 30px;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
word-wrap: break-word; /* 允许单词换行 */
max-width: 300px; /* 设置最大宽度 */
}
th {
background-color: #f2f2f2;
}
tr:hover {
background-color: #f1f1f1;
}

67
app/templates/index.html Normal file
View File

@ -0,0 +1,67 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>License Server Information</title>
<link rel="stylesheet" href="/static/styles.css">
</head>
<body>
<h1>JetBrains License Server Info Fetcher</h1>
<form id="server-form" method="post" action="/fetch">
<label for="server">Enter Server URL:</label>
<input type="text" id="server" name="server" placeholder="https://your-server.com" required>
<button type="submit">Fetch Data</button>
</form>
<br>
<div align="center">
{% if obtain_data.response_code == "" %}
<h3> Please input your server to check. </h3>
{% elif obtain_data.error %}
<h3 style="color: red;">{{ obtain_data.error }}</h3> <!-- 显示错误信息 -->
{% elif obtain_data.response_code == "OK" %}
<h3> This server is available. </h3>
{% else %}
<h3> This server is not available: {{ obtain_data.message }} </h3>
{% endif %}
</div>
<hr>
<div id="results" align="center">
{% if release_data and obtain_data %}
<h2>Release Ticket Data</h2>
<table>
<tr><th>Field</th><th>Value</th></tr>
<tr><td>Action</td><td>{{ release_data.action }}</td></tr>
<tr><td>Confirmation Stamp</td><td>{{ release_data.confirmation_stamp }}</td></tr>
<tr><td>Lease Signature</td><td>{{ release_data.lease_signature }}</td></tr>
<tr><td>Response Code</td><td>{{ release_data.response_code }}</td></tr>
<tr><td>Salt</td><td>{{ release_data.salt }}</td></tr>
<tr><td>Server Lease</td><td>{{ release_data.server_lease }}</td></tr>
<tr><td>Server UID</td><td>{{ release_data.server_uid }}</td></tr>
<tr><td>Validation Deadline Period</td><td>{{ release_data.validation_deadline_period }}</td></tr>
<tr><td>Validation Period</td><td>{{ release_data.validation_period }}</td></tr>
</table>
<h2>Obtain Ticket Data</h2>
<table>
<tr><th>Field</th><th>Value</th></tr>
<tr><td>Action</td><td>{{ obtain_data.action }}</td></tr>
<tr><td>Confirmation Stamp</td><td>{{ obtain_data.confirmation_stamp }}</td></tr>
<tr><td>Lease Signature</td><td>{{ obtain_data.lease_signature }}</td></tr>
<tr><td>Message</td><td>{{ obtain_data.message }}</td></tr>
<tr><td>Prolongation Period</td><td>{{ obtain_data.prolongation_period }}</td></tr>
<tr><td>Response Code</td><td>{{ obtain_data.response_code }}</td></tr>
<tr><td>Salt</td><td>{{ obtain_data.salt }}</td></tr>
<tr><td>Server Lease</td><td>{{ obtain_data.server_lease }}</td></tr>
<tr><td>Server UID</td><td>{{ obtain_data.server_uid }}</td></tr>
<tr><td>Validation Deadline Period</td><td>{{ obtain_data.validation_deadline_period }}</td></tr>
<tr><td>Validation Period</td><td>{{ obtain_data.validation_period }}</td></tr>
</table>
{% endif %}
</div>
<script src="/static/scripts.js"></script>
</body>
</html>

5
requirements.txt Normal file
View File

@ -0,0 +1,5 @@
fastapi
uvicorn
httpx
jinja2
python-multipart