GET /api/adr?project=X — get ADR content for a project */
| 760 | |
| 761 | /* GET /api/adr?project=X — get ADR content for a project */ |
| 762 | static void handle_adr_get(cbm_http_conn_t *c, const cbm_http_req_t *req) { |
| 763 | char name[256] = {0}; |
| 764 | if (!cbm_http_query_param(req->query, "project", name, (int)sizeof(name)) || name[0] == '\0') { |
| 765 | cbm_http_replyf(c, 400, g_cors_json, "{\"error\":\"missing project\"}"); |
| 766 | return; |
| 767 | } |
| 768 | |
| 769 | char db_path[1024]; |
| 770 | db_path_for_project(name, db_path, sizeof(db_path)); |
| 771 | |
| 772 | cbm_store_t *store = cbm_store_open_path(db_path); |
| 773 | if (!store) { |
| 774 | cbm_http_replyf(c, 200, g_cors_json, "{\"has_adr\":false}"); |
| 775 | return; |
| 776 | } |
| 777 | |
| 778 | cbm_adr_t adr; |
| 779 | memset(&adr, 0, sizeof(adr)); |
| 780 | if (cbm_store_adr_get(store, name, &adr) == CBM_STORE_OK && adr.content) { |
| 781 | /* Escape content for JSON — simple: replace quotes and newlines */ |
| 782 | size_t clen = strlen(adr.content); |
| 783 | size_t buf_size = clen * 2 + 256; |
| 784 | char *buf = malloc(buf_size); |
| 785 | if (buf) { |
| 786 | int pos = snprintf(buf, buf_size, "{\"has_adr\":true,\"content\":\""); |
| 787 | for (size_t i = 0; i < clen && (size_t)pos < buf_size - 10; i++) { |
| 788 | char ch = adr.content[i]; |
| 789 | if (ch == '"') { |
| 790 | buf[pos++] = '\\'; |
| 791 | buf[pos++] = '"'; |
| 792 | } else if (ch == '\\') { |
| 793 | buf[pos++] = '\\'; |
| 794 | buf[pos++] = '\\'; |
| 795 | } else if (ch == '\n') { |
| 796 | buf[pos++] = '\\'; |
| 797 | buf[pos++] = 'n'; |
| 798 | } else if (ch == '\r') { /* skip */ |
| 799 | } else if (ch == '\t') { |
| 800 | buf[pos++] = '\\'; |
| 801 | buf[pos++] = 't'; |
| 802 | } else { |
| 803 | buf[pos++] = ch; |
| 804 | } |
| 805 | } |
| 806 | http_appendf(buf, buf_size, &pos, "\",\"updated_at\":\"%s\"}", |
| 807 | adr.updated_at ? adr.updated_at : ""); |
| 808 | cbm_http_replyf(c, 200, g_cors_json, "%s", buf); |
| 809 | free(buf); |
| 810 | } else { |
| 811 | cbm_http_replyf(c, 500, g_cors, "oom"); |
| 812 | } |
| 813 | cbm_store_adr_free(&adr); |
| 814 | } else { |
| 815 | cbm_http_replyf(c, 200, g_cors_json, "{\"has_adr\":false}"); |
| 816 | } |
| 817 | cbm_store_close(store); |
| 818 | } |
| 819 |
no test coverage detected