Skip to content

Commit

Permalink
Make admin interface work on localhost and http.
Browse files Browse the repository at this point in the history
  • Loading branch information
ibz committed May 30, 2024
1 parent 024c546 commit a03570b
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 52 deletions.
8 changes: 2 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,13 +193,11 @@ Ways you can post to your site:

A simple REST API exists that can be used to create new sites and list sites associated with a Nostr pubkey.

In order to activate the API, you need to pass `--admin-domain <ADMIN_DOMAIN>`. Servus will listen to that domain for API requests.

### `/api/sites`

A `POST` to `https://<ADMIN_DOMAIN>/api/sites` can be used to add a new site associated with a key.
A `POST` to `/api/sites` can be used to add a new site associated with a key.

A `GET` to `https://<ADMIN_DOMAIN>/api/sites` can be used to get a list of all the sites belonging to a key.
A `GET` to `/api/sites` can be used to get a list of all the sites belonging to a key.

NB: Both requests require a [NIP-98](https://github.com/nostr-protocol/nips/blob/master/98.md) authorization header to be present, which will be validated and used to decide which Nostr pubkey the request is referring to!

Expand All @@ -209,8 +207,6 @@ Servus also implements the [Blossom API](https://github.com/hzrd149/blossom) and

## Admin interface

The same `--admin-domain <ADMIN_DOMAIN>` flag used to activate the REST API is also used to activate... you guessed it... the *admin interface*!

The *admin interface* requires you to have a Nostr extension such as [Alby](https://getalby.com/) or [nos2x](https://github.com/fiatjaf/nos2x) installed in your browser and lets you create sites, create posts and edit posts. Still very experimental, even more so than **Servus** itself!

## Any questions?
Expand Down
32 changes: 24 additions & 8 deletions admin/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,26 @@
});
}

function getWebSocketUrl(siteDomain) {
if (API_BASE_URL.startsWith("//localhost:")) {
return `ws:${API_BASE_URL}`;
} else {
return `${WS_PROTOCOL}//${site.domain}`;
}
}

function getBlossomBaseUrl(siteDomain) {
if (API_BASE_URL.startsWith("//localhost:")) {
return `http:${API_BASE_URL}`;
} else {
return `${window.location.protocol}//${siteDomain}`;
}
}

function getPosts(sites, posts) {
posts.length = 0;
for (let site of sites) {
let ws = new WebSocket(`${WS_PROTOCOL}//${site.domain}`);
let ws = new WebSocket(getWebSocketUrl(site.domain));
ws.onmessage = (e) => {
let r = JSON.parse(e.data);
if (r[0] === 'EVENT') {
Expand Down Expand Up @@ -94,7 +110,7 @@
}

function savePost(post, kind, published_at) {
let ws = new WebSocket(`${WS_PROTOCOL}//${post.site.domain}`);
let ws = new WebSocket(getWebSocketUrl(post.site.domain));
ws.onopen = async (e) => {
if (post.id === undefined) {
post.id = post.title.toLowerCase().replace(/ /g, "-").replace(/[^\w-]+/g, "");
Expand All @@ -109,7 +125,7 @@
}

function deleteResource(post, event_id) {
let ws = new WebSocket(`${WS_PROTOCOL}//${post.site.domain}`);
let ws = new WebSocket(getWebSocketUrl(post.site.domain));
ws.onopen = async (e) => {
ws.send(JSON.stringify(['EVENT', await getEvent(5, "", [['e', event_id]])]));
post.persisted = false;
Expand All @@ -119,7 +135,7 @@
function getNotes(site, notes) {
notes.length = 0;

let ws = new WebSocket(`${WS_PROTOCOL}//${site.domain}`);
let ws = new WebSocket(getWebSocketUrl(site.domain));
ws.onmessage = (e) => {
let r = JSON.parse(e.data);
if (r[0] === 'EVENT') {
Expand All @@ -136,21 +152,21 @@
async function getFiles(site, files) {
files.length = 0;

const res = await fetch(new URL(`${window.location.protocol}//${site.domain}/list/${await window.nostr.getPublicKey()}`));
const res = await fetch(new URL(`${getBlossomBaseUrl(site.domain)}/list/${await window.nostr.getPublicKey()}`));
for (f of await res.json()) {
files.push(f);
}
}

function saveNote(note) {
let ws = new WebSocket(`${WS_PROTOCOL}//${note.site.domain}`);
let ws = new WebSocket(getWebSocketUrl(note.site.domain));
ws.onopen = async (e) => {
ws.send(JSON.stringify(['EVENT', await getEvent(EVENT_KIND_NOTE, note.content, [])]));
};
}

async function uploadFileBlossom(site) {
const endpoint = `${window.location.protocol}//${site.domain}/upload`;
const endpoint = `${getBlossomBaseUrl(site.domain)}/upload`;
let fileInput = document.querySelector('#fileInput');
const res = await fetch(new URL(endpoint), {
method: "PUT",
Expand All @@ -161,7 +177,7 @@
}

async function deleteFile(site, sha256) {
let endpoint = `${window.location.protocol}//${site.domain}/${sha256}`;
let endpoint = `${getBlossomBaseUrl(site.domain)}/${sha256}`;
await fetch(new URL(endpoint),
{
method: 'DELETE',
Expand Down
61 changes: 23 additions & 38 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ use site::Site;

#[derive(Parser)]
struct Cli {
#[clap(short('a'), long)]
admin_domain: Option<String>,

#[clap(short('e'), long)]
contact_email: Option<String>,

Expand All @@ -53,7 +50,6 @@ struct Cli {

#[derive(Clone)]
struct State {
admin_domain: Option<String>,
sites: Arc<RwLock<HashMap<String, Site>>>,
}

Expand Down Expand Up @@ -234,20 +230,6 @@ async fn handle_websocket(
}

async fn handle_index(request: Request<State>) -> tide::Result<Response> {
let state = &request.state();

if state.admin_domain.is_some() {
let admin_domain = state.admin_domain.to_owned().unwrap();
if *request.host().unwrap() == admin_domain {
let admin_index =
admin::INDEX_HTML.replace("%%API_BASE_URL%%", &format!("//{}", admin_domain));
return Ok(Response::builder(StatusCode::Ok)
.content_type(mime::HTML)
.body(admin_index)
.build());
}
}

if let Some(site) = get_site(&request) {
let resources = site.resources.read().unwrap();
match resources.get("/index") {
Expand Down Expand Up @@ -280,6 +262,17 @@ async fn handle_request(request: Request<State>) -> tide::Result<Response> {
path = path.strip_suffix('/').unwrap();
}

if path == ".admin" {
let admin_index = admin::INDEX_HTML.replace(
"%%API_BASE_URL%%",
&format!("//{}", request.host().unwrap()),
);
return Ok(Response::builder(StatusCode::Ok)
.content_type(mime::HTML)
.body(admin_index)
.build());
}

let mut part: Option<String> = None;
if path.contains(".") {
let parts = path.split(".").collect::<Vec<_>>();
Expand Down Expand Up @@ -401,16 +394,6 @@ async fn handle_post_site(mut request: Request<State>) -> tide::Result<Response>
.domain;
let state = &request.state();

if state.admin_domain.is_none() {
return Ok(Response::builder(StatusCode::NotFound).build());
}

let admin_domain = state.admin_domain.to_owned().unwrap();

if *request.host().unwrap() != admin_domain {
return Ok(Response::builder(StatusCode::NotFound).build());
}

if state.sites.read().unwrap().contains_key(&domain) {
Ok(Response::builder(StatusCode::Conflict).build())
} else {
Expand Down Expand Up @@ -661,8 +644,9 @@ async fn main() -> Result<(), std::io::Error> {
sites = existing_sites;
}

let site_count = sites.len();

let mut app = tide::with_state(State {
admin_domain: args.admin_domain.clone(),
sites: Arc::new(RwLock::new(sites)),
});

Expand All @@ -676,11 +660,9 @@ async fn main() -> Result<(), std::io::Error> {
.put(handle_upload_request);
app.at("/list/:pubkey").get(handle_list_request);
app.at("/:sha256").delete(handle_delete_request);
if args.admin_domain.is_some() {
app.at("/api/sites")
.post(handle_post_site)
.get(handle_get_sites);
}
app.at("/api/sites")
.post(handle_post_site)
.get(handle_get_sites);

let addr = "0.0.0.0";

Expand All @@ -696,17 +678,14 @@ async fn main() -> Result<(), std::io::Error> {
if args.contact_email.is_none() {
panic!("Use -e to provide a contact email!");
}
let mut domains: Vec<String> = app
let domains: Vec<String> = app
.state()
.sites
.read()
.unwrap()
.keys()
.map(|x| x.to_string())
.collect();
if args.admin_domain.is_some() {
domains.push(args.admin_domain.unwrap());
}
let cache = DirCache::new("./cache");
let acme_config = AcmeConfig::new(domains)
.cache(cache)
Expand All @@ -723,6 +702,12 @@ async fn main() -> Result<(), std::io::Error> {
} else {
let port = args.port.unwrap_or(4884);
let bind_to = format!("{addr}:{port}");
println!("####################################");
if site_count == 1 {
println!("*** Your site: http://localhost:{port}/ ***");
}
println!("*** The admin interface: http://localhost:{port}/.admin/ ***");
println!("####################################");
app.listen(bind_to).await?;
};

Expand Down

0 comments on commit a03570b

Please sign in to comment.