Skip to content

Commit

Permalink
automate schema generation
Browse files Browse the repository at this point in the history
  • Loading branch information
mmmray committed Sep 22, 2024
1 parent cd959b2 commit c4f2832
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 85 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
go-version: '1.23'
check-latest: true
- run: make build
- run: mkdir public && cp index.html main.wasm wasm_exec.js xray-schema.json public
- run: mkdir public && cp index.html main.wasm wasm_exec.js xray.schema.json public
- name: Upload artifact
uses: actions/upload-pages-artifact@v1
with:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.wasm
*.js
assets
*.schema.json
9 changes: 8 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
build: main.wasm wasm_exec.js
build: main.wasm wasm_exec.js xray.schema.json
.PHONY: build

dev-lite:
Expand Down Expand Up @@ -30,5 +30,12 @@ wasm_exec.js:
main.wasm: assets/geoip.dat assets/geosite.dat assets/xray-patched main.go go.mod xray-wasm.patch
GOARCH=wasm GOOS=js go build -o main.wasm main.go

xray.schema.json:
rm -rf assets/xray-docs-next
mkdir -p assets
cd assets && git clone --depth 1 https://github.com/xtls/xray-docs-next
rg '' assets/xray-docs-next/docs/en/config/ | cut -d: -f2- | python3 scrape-docs.py > xray.schema.json


serve:
python3 -mhttp.server
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ <h1>Xray Config Validator</h1>
return error.message;
}
}
fetch('xray-schema.json')
fetch('xray.schema.json')
.then(response => response.json())
.then(schema => {
monaco.languages.json.jsonDefaults.setDiagnosticsOptions({
Expand Down
134 changes: 134 additions & 0 deletions scrape-docs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import os
import json
import sys

from typing import Iterator, Sequence, TypedDict

class RawType(TypedDict):
title: str
description: str
raw_properties: list[dict]

class JsonschemaType(TypedDict):
title: str
description: str
properties: dict[str, dict]

def parse(stdin: Iterator[str]) -> Iterator[JsonschemaType]:
current_obj: RawType | None = None

for line in stdin:
if not line.strip():
continue
elif line.startswith("## "):
if current_obj:
yield {
"title": current_obj['title'],
"description": current_obj['description'],
"properties": {
x['name']: x
for x in current_obj['raw_properties']
}
}

current_obj = {
"title": line[2:].strip(),
"description": "",
"raw_properties": []
}
elif line.startswith("> ") and ":" in line and current_obj:
name, ty = line[2:].split(":", 1)
if name == "Tony":
continue

name = name.strip(" `")

current_obj['raw_properties'].append({
"name": name,
"description": "",
**parse_type(ty),
})
elif current_obj:
if current_obj['raw_properties']:
current_obj['raw_properties'][-1]['description'] += line
else:
current_obj['description'] += line

def parse_type(input: str) -> dict:
input = input \
.replace('<Badge text="WIP" type="warning"/>', '') \
.replace('<Badge text="BETA" type="warning"/>', '') \
.strip()

if not input:
return {}

if input.startswith("\\[") and input.endswith("\\]"):
return {
"type": "array",
"items": parse_type(input[2:-2])
}

if input.startswith("[") and input.endswith("]"):
return {
"type": "array",
"items": parse_type(input[1:-1])
}

if input.startswith("[") and input.endswith(")"):
name = input.split("]")[0].strip("[]")
return {
"$ref": f"#/definitions/{name}"
}

if input in ("true", "false", "true | false", "bool"):
return {"type": "bool"}

if " | " in input:
return {"anyOf": [parse_type(x) for x in input.split(" | ")]}

if input.endswith("Object"):
return {
"$ref": f"#/definitions/{input}"
}

if input in ("address", "address_port", "CIDR"):
return {"type": "string"}

if input in ("string", "number"):
return {"type": input}

if input == "int/string":
return parse_type("int | string")

if input == "int":
return {"type": "integer"}

if input.startswith("map"):
return {"type": "object"}

if input.startswith('"') and input.endswith('"'):
return {"const": input[1:-1]}

if input.startswith("a list of"):
return {}

if input == "string array":
return {"type": "array", "items": {"type": "string"}}

if input.startswith("string, any of"):
return {"type": "string"}

raise Exception(input)

def main():
schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"$ref": "#/definitions/Basic Configuration Modules",
"definitions": {x['title']: x for x in parse(sys.stdin)},
}

print(json.dumps(schema, indent=2))

if __name__ == "__main__":
main()
82 changes: 0 additions & 82 deletions xray-schema.json

This file was deleted.

0 comments on commit c4f2832

Please sign in to comment.