mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-04-24 03:45:38 +08:00
cleaning up subdomain rest api + documentation
This commit is contained in:
@@ -469,9 +469,6 @@ def subdomain_record_to_profile(my_rec):
|
||||
if user_data_pubkey is None:
|
||||
user_data_pubkey = owner_pubkey.to_hex()
|
||||
|
||||
print("Fetching profile from {} with pubkey {}".format(
|
||||
urls, user_data_pubkey))
|
||||
|
||||
user_profile = storage.get_mutable_data(
|
||||
None, user_data_pubkey, blockchain_id=None,
|
||||
data_address=None, owner_address=None,
|
||||
|
||||
@@ -66,43 +66,154 @@ At 4kb zonefile size, we can only fit around 20 updates per zonefile.
|
||||
|
||||
### Domain Operator Endpoint
|
||||
|
||||
We'll need to provide an API endpoint for sending operations to the
|
||||
domain operator *and* an interface for sending commands to that
|
||||
endpoint.
|
||||
|
||||
Operating a domain should be something that anyone running a Core node
|
||||
should be able to do with a simple command:
|
||||
The directory `subdomain_registrar/` contains our code for running a
|
||||
subdomain registrar. It can be executed by running:
|
||||
|
||||
```
|
||||
$ blockstack domain foo.id start
|
||||
$ blockstack-subdomain-registrar start foo.id
|
||||
```
|
||||
|
||||
Here, `foo.id` is the domain for which subdomains will be associated.
|
||||
|
||||
#### AddSubdomain Command
|
||||
#### Configuration and Registration Files
|
||||
|
||||
Configuration of the subdomain registrar is done through `~/.blockstack_subdomains/config.ini`
|
||||
|
||||
The sqlite database which stores the registrations is located alongside the config `~/.blockstack_subdomains/registrar.db`.
|
||||
|
||||
You can change the location of the config file (and the database), by setting the environment variable `BLOCKSTACK_SUBDOMAIN_CONFIG`
|
||||
|
||||
#### Register Subdomain
|
||||
|
||||
Subdomain registrations can be submitted to this endpoint using a REST
|
||||
API.
|
||||
|
||||
```
|
||||
addSubdomain("foo", "bar.id", pubkey_hex, urls)
|
||||
POST /register
|
||||
```
|
||||
|
||||
This command adds a subdomain `foo` to a domain `bar.id`. This will:
|
||||
The schema for registration is:
|
||||
|
||||
```
|
||||
{
|
||||
'type' : 'object',
|
||||
'properties' : {
|
||||
'subdomain' : {
|
||||
'type': 'string',
|
||||
'pattern': '([a-z0-9\-_+]{3,36})$'
|
||||
},
|
||||
'data_pubkey' : {
|
||||
'type': 'string',
|
||||
'pattern': r'^(pubkey:data:[0-9a-fA-F]+)$'
|
||||
},
|
||||
'uris' : {
|
||||
'type': 'array',
|
||||
'items':
|
||||
{
|
||||
'type': 'object',
|
||||
'properties': {
|
||||
'name': {
|
||||
'type': 'string'
|
||||
},
|
||||
'priority': {
|
||||
'type': 'integer',
|
||||
'minimum': 0,
|
||||
'maximum': 65535,
|
||||
},
|
||||
'weight': {
|
||||
'type': 'integer',
|
||||
'minimum': 0,
|
||||
'maximum': 65535,
|
||||
},
|
||||
'target': {
|
||||
'anyOf': [
|
||||
{
|
||||
'type': 'string',
|
||||
'pattern': '^([a-z0-9+]+)://([a-zA-Z0-9\-_.~%#?&\\:/=]+)$'
|
||||
},
|
||||
{
|
||||
'type': 'string',
|
||||
'pattern': '^([a-zA-Z0-9\-_.~%#?&\\:/=]+)$'
|
||||
},
|
||||
],
|
||||
},
|
||||
'class': {
|
||||
'type': 'string'
|
||||
},
|
||||
'_missing_class': {
|
||||
'type': 'boolean'
|
||||
},
|
||||
},
|
||||
'required': [
|
||||
'name',
|
||||
'priority',
|
||||
'weight',
|
||||
'target'
|
||||
],
|
||||
}
|
||||
},
|
||||
'zonefile_str' : {
|
||||
'type' : 'string',
|
||||
'maxLength' : 4096
|
||||
}
|
||||
}
|
||||
'required': ['data_pubkey', 'subdomain']
|
||||
}
|
||||
```
|
||||
|
||||
The request supplies *either* a list of URIs for the subdomain,
|
||||
or a raw zonefile for the subdomain.
|
||||
|
||||
The registrar will:
|
||||
|
||||
1. Check if the subdomain `foo` exists already on the domain.
|
||||
2. Add a record to the zonefile.
|
||||
3. Issue zonefile update.
|
||||
2. Add the subdomain to the queue.
|
||||
|
||||
#### UpdateSubdomain Command
|
||||
On success, this returns `202` and the message
|
||||
|
||||
```
|
||||
updateSubdomain("foo", "bar.id", pubkey_hex, n, urls, signature)
|
||||
{"status": "true", "message": "Subdomain registration queued."}
|
||||
```
|
||||
|
||||
This command updates subdomain `foo` to a domain `bar.id`. This will:
|
||||
When the registrar wakes up to prepare a transaction, it packs the queued
|
||||
registrations together and issues an `UPDATE`.
|
||||
|
||||
1. Check if the subdomain `foo` exists already on the domain
|
||||
2. Check that n = n' + 1
|
||||
3. Check the signature
|
||||
4. Issue zonefile update
|
||||
|
||||
#### Check subdomain registration status
|
||||
|
||||
A user can check on the registration status of their name via querying the
|
||||
registrar.
|
||||
|
||||
This is an API call:
|
||||
```
|
||||
GET /status/{subdomain}
|
||||
```
|
||||
|
||||
The registrar checks if the subdomain has propagated (i.e., the
|
||||
registration is completed), in which case the following is returned:
|
||||
|
||||
```
|
||||
{"status": "Subdomain already propagated"}
|
||||
```
|
||||
|
||||
Or, if the subdomain has already been submitted in a transaction:
|
||||
|
||||
```
|
||||
{"status": "Your subdomain was registered in transaction 09a40d6ea362608c68da6e1ebeb3210367abf7aa39ece5fd57fd63d269336399 -- it should propagate on the network once it has 6 confirmations."}
|
||||
```
|
||||
|
||||
If the subdomain still hasn't been submitted yet:
|
||||
|
||||
```
|
||||
{"status": "Subdomain is queued for update and should be announced within the next few blocks."}
|
||||
```
|
||||
|
||||
If an error occurred trying to submit the `UPDATE` transaction, this endpoint will return an error
|
||||
message in the `"error"` key of a JSON object.
|
||||
|
||||
#### Updating Entries
|
||||
|
||||
The subdomain registrar does not currently support updating subdomain entries.
|
||||
|
||||
### Resolver Behavior
|
||||
|
||||
@@ -126,7 +237,8 @@ all the current subdomain records.
|
||||
1. Testing bad zonefile transitions / updates.
|
||||
a. Wrong _n_ : this could be a rewrite, roll-back, whatever. [x]
|
||||
b. Bad signature [x]
|
||||
2. Caching resolver database [o]
|
||||
3. Batching updates [o]
|
||||
4. Web API [o]
|
||||
5. Endpoint support for changing zonefiles/rotating keys
|
||||
2. Caching resolver database [x]
|
||||
3. Batching updates [x]
|
||||
4. Web API [x]
|
||||
5. Resolver database cache for holding *multiple* domains, instead of just one [o]
|
||||
6. Endpoint support for changing zonefiles/rotating keys [o]
|
||||
|
||||
@@ -356,40 +356,45 @@ class SubdomainRegistrarRPCWorker(threading.Thread):
|
||||
self.server.serve_forever()
|
||||
|
||||
class SubdomainRegistrarRPCHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
def send_message(self, code, message):
|
||||
self.send_response(code)
|
||||
self.send_header("Content-Type", "application/json")
|
||||
self.end_headers()
|
||||
self.wfile.write(message + "\r\n")
|
||||
|
||||
def do_GET(self):
|
||||
if not str(self.path).startswith("/status/"):
|
||||
return self.send_response(404, json.dumps({"error" : "Unsupported API method"}))
|
||||
return self.send_message(404, json.dumps({"error" : "Unsupported API method"}))
|
||||
name = self.path[len("/status/"):]
|
||||
if re.match(config.SUBDOMAIN_NAME_PATTERN, name) is None:
|
||||
return self.send_response(404, json.dumps({"error" : "Invalid subdomain supplied"}))
|
||||
return self.send_message(404, json.dumps({"error" : "Invalid subdomain supplied"}))
|
||||
status = get_queued_name(name, self.server.domain_name)
|
||||
if "error" in status:
|
||||
status_code = status.get("status_code", 500)
|
||||
return self.send_response(status_code, json.dumps({"error": status["error"]}))
|
||||
return self.send_response(200, json.dumps(status))
|
||||
return self.send_message(status_code, json.dumps({"error": status["error"]}))
|
||||
return self.send_message(200, json.dumps(status))
|
||||
|
||||
def do_POST(self):
|
||||
self.send_header("Content-Type", "application/json")
|
||||
if str(self.path) != "/register":
|
||||
return self.send_response(404, json.dumps({"error" : "Unsupported API method"}))
|
||||
return self.send_message(404, json.dumps({"error" : "Unsupported API method"}))
|
||||
length = int(self.headers.getheader('content-length'))
|
||||
if length > 1024 * 1024:
|
||||
return self.send_response(403, json.dumps({"error" : "Content length too long. Request Denied."}))
|
||||
return self.send_message(403, json.dumps({"error" : "Content length too long. Request Denied."}))
|
||||
try:
|
||||
subdomain = parse_subdomain_request(self.rfile.read(length))
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
return self.send_response(401, json.dumps({"error" : "Problem parsing request"}))
|
||||
return self.send_message(401, json.dumps({"error" : "Problem parsing request"}))
|
||||
|
||||
try:
|
||||
queued_resp = queue_name_for_registration(subdomain, self.server.domain_name)
|
||||
except subdomains.SubdomainAlreadyExists as e:
|
||||
log.exception(e)
|
||||
return self.send_response(403, json.dumps({"error" : "Subdomain already exists on this domain"}))
|
||||
return self.send_message(403, json.dumps({"error" : "Subdomain already exists on this domain"}))
|
||||
|
||||
if "error" in queued_resp:
|
||||
return self.send_response(500, json.dumps(queued_resp))
|
||||
return self.send_response(202, json.dumps(queued_resp))
|
||||
return self.send_message(500, json.dumps(queued_resp))
|
||||
return self.send_message(202, json.dumps(queued_resp))
|
||||
|
||||
class SubdomainLock(object):
|
||||
@staticmethod
|
||||
|
||||
Reference in New Issue
Block a user