package ins.frontend;
import ins.frontend.utils.RegistrarDao;
import ins.frontend.utils.WebApp;
import org.openautonomousconnection.protocol.packets.v1_0_0.beta.web.WebResponsePacket;
import org.openautonomousconnection.protocol.side.web.ProtocolWebServer;
import org.openautonomousconnection.webserver.api.Route;
import org.openautonomousconnection.webserver.api.SessionContext;
import org.openautonomousconnection.webserver.api.WebPage;
import org.openautonomousconnection.webserver.api.WebPageContext;
import org.openautonomousconnection.webserver.utils.Html;
import org.openautonomousconnection.webserver.utils.MergedRequestParams;
import org.openautonomousconnection.webserver.utils.QuerySupport;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* INS registrar ins.frontend (TLN / InfoName / Records) with proper POST parameter parsing.
*
*
Supported actions (POST recommended for mutations):
*
* - create_tln
* - update_tln
* - delete_tln
* - create_infoname
* - delete_infoname
* - add_record
*
*
* Important: Listing/editing/deleting records requires DAO methods that are not part of the provided snippet.
* This page currently supports adding records only.
*/
@Route(path = "dashboard.html")
public final class dashboard implements WebPage {
private static Integer normalizeNullableInt(String s) {
if (s == null) return null;
String t = s.trim();
if (t.isEmpty()) return null;
try {
return Integer.parseInt(t);
} catch (Exception ignored) {
return null;
}
}
@Override
public WebResponsePacket handle(WebPageContext ctx) throws Exception {
WebApp.init();
SessionContext session = SessionContext.from(
ctx.client,
(ProtocolWebServer) ctx.client.getServer(),
ctx.request.getHeaders()
);
if (!session.isValid() || session.getUser() == null) {
return plain(401, "Authentication required (session).");
}
int userId;
try {
userId = Integer.parseInt(session.getUser());
} catch (Exception e) {
return plain(401, "Invalid session user.");
}
RegistrarDao dao = WebApp.get().dao();
// Raw target and merged params (GET + POST).
String rawTarget = org.openautonomousconnection.webserver.utils.QuerySupport.extractRawTarget(ctx.request);
Map headers = ctx.request.getHeaders();
byte[] body = ctx.request.getBody();
MergedRequestParams p = MergedRequestParams.from(rawTarget, headers, body);
String msg = null;
String err = null;
String action = p.getOr("action", "").trim();
if (!action.isBlank()) {
try {
ActionResult r = executeAction(action, p, userId, dao);
msg = r.msg();
err = r.err();
} catch (Exception e) {
err = "Action failed: " + safeMsg(e);
}
}
return render(userId, msg, err, dao);
}
private ActionResult executeAction(String action, MergedRequestParams p, int userId, RegistrarDao dao) throws Exception {
String a = action.trim().toLowerCase(Locale.ROOT);
if ("create_tln".equals(a)) {
String name = p.get("name");
String info = p.getOr("info", "");
boolean isPublic = p.getBool("is_public");
boolean allowSubs = p.getBool("allow_subdomains");
if (name == null || name.isBlank()) return ActionResult.err("Missing TLN name.");
int id = dao.createTln(name.trim(), info, userId, isPublic, allowSubs);
return ActionResult.ok("Created TLN id=" + id + " (" + name.trim() + ")");
}
if ("update_tln".equals(a)) {
int id = p.getInt("id", -1);
String info = p.getOr("info", "");
boolean isPublic = p.getBool("is_public");
boolean allowSubs = p.getBool("allow_subdomains");
if (id <= 0) return ActionResult.err("Invalid TLN id.");
boolean ok = dao.updateTlnOwned(id, userId, info, isPublic, allowSubs);
return ActionResult.ok(ok ? ("Updated TLN id=" + id) : "Not owner / not found.");
}
if ("delete_tln".equals(a)) {
int id = p.getInt("id", -1);
if (id <= 0) return ActionResult.err("Invalid TLN id.");
boolean ok = dao.deleteTlnOwned(id, userId);
return ActionResult.ok(ok ? ("Deleted TLN id=" + id) : "Not owner / not found.");
}
if ("create_infoname".equals(a)) {
String tlnName = p.get("tln");
String info = p.get("info");
if (tlnName == null || tlnName.isBlank() || info == null || info.isBlank()) {
return ActionResult.err("Missing tln / info.");
}
RegistrarDao.TlnRow tln = dao.findTlnByName(tlnName.trim()).orElse(null);
if (tln == null) return ActionResult.err("Unknown TLN: " + tlnName);
if (!RegistrarDao.canUseTln(tln, userId)) {
return ActionResult.err("TLN not public and not owned by you.");
}
int id = dao.createInfoName(tln, info.trim(), userId);
return ActionResult.ok("Created infoname id=" + id + " (" + info.trim() + "." + tln.name() + ")");
}
if ("delete_infoname".equals(a)) {
int id = p.getInt("id", -1);
if (id <= 0) return ActionResult.err("Invalid infoname id.");
boolean ok = dao.deleteInfoNameOwned(id, userId);
return ActionResult.ok(ok ? ("Deleted infoname id=" + id) : "Not owner / not found.");
}
if ("add_record".equals(a)) {
int infonameId = p.getInt("infoname_id", -1);
String sub = p.get("sub");
String type = p.getOr("type", "").trim().toUpperCase(Locale.ROOT);
String value = p.get("value");
int ttl = p.getInt("ttl", 3600);
Integer priority = normalizeNullableInt(p.get("priority"));
Integer port = normalizeNullableInt(p.get("port"));
Integer weight = normalizeNullableInt(p.get("weight"));
if (infonameId <= 0) return ActionResult.err("Invalid infoname_id.");
if (!dao.isOwnerOfInfoName(infonameId, userId)) return ActionResult.err("Not owner of this infoname.");
if (type.isBlank() || value == null || value.isBlank()) return ActionResult.err("Missing type/value.");
// Validate allow_subdomains against TLN of this infoname (owned list contains TLN metadata).
RegistrarDao.InfoNameRow[] owned = dao.listOwnedInfoNames(userId);
RegistrarDao.InfoNameRow row = null;
for (RegistrarDao.InfoNameRow r : owned) {
if (r.id() == infonameId) {
row = r;
break;
}
}
if (row == null) return ActionResult.err("Infoname not found in your ownership list.");
if (!RegistrarDao.canUseSubname(row.tln(), userId, sub)) {
return ActionResult.err("Subnames are not allowed for this TLN (allow_subdomains=0) unless you own the TLN.");
}
Integer subId = dao.ensureSubname(infonameId, sub);
int rid = dao.addRecord(infonameId, subId, type, value.trim(), ttl, priority, port, weight);
return ActionResult.ok("Added record id=" + rid + " type=" + type);
}
return ActionResult.err("Unknown action: " + action);
}
private WebResponsePacket render(int userId, String msg, String err, RegistrarDao dao) throws Exception {
RegistrarDao.TlnRow[] tlns = dao.listVisibleTlns(userId);
RegistrarDao.InfoNameRow[] owned = dao.listOwnedInfoNames(userId);
String tlnHtml = renderTlnSection(tlns, userId);
String infoHtml = renderInfoNamesSection(owned);
String body = """
INS Registrar
%s
%s
Create TLN
TLNs (public + owned)
%s
Create InfoName
Allowed if TLN is public or owned by you.
Add Record
Subname requires allow_subdomains=1 unless you own the TLN. Root (no sub) always allowed.
Your InfoNames
%s
""".formatted(
msg == null ? "" : "" + Html.esc(msg) + "
",
err == null ? "" : "" + Html.esc(err) + "
",
tlnHtml,
infoHtml
);
String html = Html.page("INS Registrar", body);
return new WebResponsePacket(200, "text/html", new HashMap<>(), Html.utf8(html));
}
private String renderTlnSection(RegistrarDao.TlnRow[] tlns, int userId) {
if (tlns == null || tlns.length == 0) {
return "No TLNs available.
";
}
StringBuilder sb = new StringBuilder();
sb.append("");
return sb.toString();
}
private String renderInfoNamesSection(RegistrarDao.InfoNameRow[] owned) {
if (owned == null || owned.length == 0) {
return "No infonames yet.
";
}
StringBuilder sb = new StringBuilder();
sb.append("");
for (RegistrarDao.InfoNameRow r : owned) {
sb.append("- ")
.append("
")
.append(Html.esc(r.info())).append(".").append(Html.esc(r.tln().name()))
.append("")
.append(" (id=").append(r.id()).append(")")
.append("""
""".formatted(r.id()))
.append(" ");
}
sb.append("
");
return sb.toString();
}
private WebResponsePacket plain(int code, String text) {
return new WebResponsePacket(code, "text/plain", new HashMap<>(), Html.utf8(text));
}
private String safeMsg(Exception e) {
String m = e.getMessage();
if (m == null || m.isBlank()) return e.getClass().getSimpleName();
return m;
}
private record ActionResult(String msg, String err) {
static ActionResult ok(String msg) {
return new ActionResult(msg, null);
}
static ActionResult err(String err) {
return new ActionResult(null, err);
}
}
}