users balance add

This commit is contained in:
SimonFJ20 2025-03-17 13:44:48 +01:00
parent b811c208c9
commit 974f057dc1
9 changed files with 101 additions and 22 deletions

View File

@ -40,12 +40,13 @@ void route_get_products_all(HttpCtx* ctx);
void route_post_carts_purchase(HttpCtx* ctx); void route_post_carts_purchase(HttpCtx* ctx);
void route_post_users_register(HttpCtx* ctx); void route_post_users_register(HttpCtx* ctx);
void route_post_users_balance_add(HttpCtx* ctx);
void route_post_sessions_login(HttpCtx* ctx); void route_post_sessions_login(HttpCtx* ctx);
void route_post_sessions_logout(HttpCtx* ctx); void route_post_sessions_logout(HttpCtx* ctx);
void route_get_sessions_user(HttpCtx* ctx); void route_get_sessions_user(HttpCtx* ctx);
void route_get_receipt(HttpCtx* ctx); void route_get_receipts_one(HttpCtx* ctx);
const Session* header_session(HttpCtx* ctx); const Session* header_session(HttpCtx* ctx);
const Session* middleware_session(HttpCtx* ctx); const Session* middleware_session(HttpCtx* ctx);

View File

@ -40,7 +40,7 @@ void query_params_destroy(QueryParams* query_params)
query_param_vec_destroy(&query_params->vec); query_param_vec_destroy(&query_params->vec);
} }
const char* query_params_get(const QueryParams* query_params, const char* key) char* query_params_get(const QueryParams* query_params, const char* key)
{ {
size_t key_len = strlen(key); size_t key_len = strlen(key);
for (size_t i = 0; i < query_params->vec.size; ++i) { for (size_t i = 0; i < query_params->vec.size; ++i) {
@ -52,7 +52,7 @@ const char* query_params_get(const QueryParams* query_params, const char* key)
return NULL; return NULL;
} }
void route_get_receipt(HttpCtx* ctx) void route_get_receipts_one(HttpCtx* ctx)
{ {
Cx* cx = http_ctx_user_ctx(ctx); Cx* cx = http_ctx_user_ctx(ctx);
const Session* session = middleware_session(ctx); const Session* session = middleware_session(ctx);
@ -61,7 +61,7 @@ void route_get_receipt(HttpCtx* ctx)
const char* query = http_ctx_req_query(ctx); const char* query = http_ctx_req_query(ctx);
QueryParams params = parse_query_params(query); QueryParams params = parse_query_params(query);
const char* receipt_id_str = query_params_get(&params, "receipt_id"); char* receipt_id_str = query_params_get(&params, "receipt_id");
query_params_destroy(&params); query_params_destroy(&params);
if (!receipt_id_str) { if (!receipt_id_str) {
RESPOND_BAD_REQUEST(ctx, "no receipt_id parameter"); RESPOND_BAD_REQUEST(ctx, "no receipt_id parameter");
@ -69,6 +69,7 @@ void route_get_receipt(HttpCtx* ctx)
} }
int64_t receipt_id = strtol(receipt_id_str, NULL, 10); int64_t receipt_id = strtol(receipt_id_str, NULL, 10);
free(receipt_id_str);
Receipt receipt; Receipt receipt;
DbRes db_rizz = db_receipt_with_id(cx->db, &receipt, receipt_id); DbRes db_rizz = db_receipt_with_id(cx->db, &receipt, receipt_id);

View File

@ -61,24 +61,27 @@ void route_post_users_register(HttpCtx* ctx)
RESPOND_JSON(ctx, 200, "{\"ok\":true}"); RESPOND_JSON(ctx, 200, "{\"ok\":true}");
} }
void route_add_balance(HttpCtx* ctx) void route_post_users_balance_add(HttpCtx* ctx)
{ {
Cx* cx = http_ctx_user_ctx(ctx); Cx* cx = http_ctx_user_ctx(ctx);
const Session* session = middleware_session(ctx); const Session* session = middleware_session(ctx);
if (!session) if (!session)
return; return;
printf("token: %s\n user_id: %ld\n", session->token, session->user_id);
const char* body_str = http_ctx_req_body(ctx); User user;
JsonValue* body_json = json_parse(body_str, strlen(body_str)); if (db_user_with_id(cx->db, &user, session->user_id) != DbRes_Ok) {
if (!body_json) { RESPOND_SERVER_ERROR(ctx);
RESPOND_BAD_REQUEST(ctx, "bad request");
return; return;
} }
json_free(body_json);
user.balance_dkk_cent += 10000;
DbRes db_res = db_user_update(cx->db, &user);
user_destroy(&user);
if (db_res != DbRes_Ok) {
RESPOND_SERVER_ERROR(ctx);
return;
}
RESPOND_JSON(ctx, 200, "{\"ok\":true}");
} }

View File

@ -18,6 +18,10 @@ typedef struct Db Db;
/// `user.id` field is ignored. /// `user.id` field is ignored.
DbRes db_user_insert(Db* db, const User* user); DbRes db_user_insert(Db* db, const User* user);
/// Uses `user.id` to find model.
DbRes db_user_update(Db* db, const User* user);
/// `user` field is an out parameter. /// `user` field is an out parameter.
DbRes db_user_with_id(Db* db, User* user, int64_t id); DbRes db_user_with_id(Db* db, User* user, int64_t id);

View File

@ -48,7 +48,10 @@ void db_sqlite_free(Db* db)
static inline DbRes connect(sqlite3** connection) static inline DbRes connect(sqlite3** connection)
{ {
int res = sqlite3_open("database.db", connection); int res = sqlite3_open_v2("database.db",
connection,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX,
NULL);
if (res != SQLITE_OK) { if (res != SQLITE_OK) {
fprintf(stderr, fprintf(stderr,
"error: could not open sqlite 'database.db'\n %s\n", "error: could not open sqlite 'database.db'\n %s\n",
@ -84,18 +87,65 @@ DbRes db_user_insert(Db* db, const User* user)
DbRes res; DbRes res;
sqlite3_stmt* stmt; sqlite3_stmt* stmt;
sqlite3_prepare_v2(connection, int prepare_res = sqlite3_prepare_v2(connection,
"INSERT INTO users (name, email, password_hash, balance_dkk_cent) " "INSERT INTO users (name, email, password_hash, balance_dkk_cent) "
"VALUES (?, ?, ?, ?)", "VALUES (?, ?, ?, ?)",
-1, -1,
&stmt, &stmt,
NULL); NULL);
if (prepare_res != SQLITE_OK) {
REPORT_SQLITE3_ERROR();
res = DbRes_Error;
goto l0_return;
}
sqlite3_bind_text(stmt, 1, user->name, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 1, user->name, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, user->email, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 2, user->email, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 3, user->password_hash, -1, SQLITE_STATIC); sqlite3_bind_text(stmt, 3, user->password_hash, -1, SQLITE_STATIC);
sqlite3_bind_int64(stmt, 4, user->balance_dkk_cent); sqlite3_bind_int64(stmt, 4, user->balance_dkk_cent);
int step_res = sqlite3_step(stmt);
if (step_res != SQLITE_DONE) {
REPORT_SQLITE3_ERROR();
res = DbRes_Error;
goto l0_return;
}
res = DbRes_Ok;
l0_return:
if (stmt)
sqlite3_finalize(stmt);
DISCONNECT;
return res;
}
DbRes db_user_update(Db* db, const User* user)
{
static_assert(sizeof(User) == 40, "model has changed");
sqlite3* connection;
CONNECT;
DbRes res;
sqlite3_stmt* stmt;
int prepare_res = sqlite3_prepare_v2(connection,
"UPDATE users SET name = ?, email = ?, password_hash= ?, "
"balance_dkk_cent= ? WHERE id = ?",
-1,
&stmt,
NULL);
if (prepare_res != SQLITE_OK) {
REPORT_SQLITE3_ERROR();
res = DbRes_Error;
goto l0_return;
}
sqlite3_bind_text(stmt, 1, user->name, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, user->email, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 3, user->password_hash, -1, SQLITE_STATIC);
sqlite3_bind_int64(stmt, 4, user->balance_dkk_cent);
sqlite3_bind_int64(stmt, 5, user->id);
int step_res = sqlite3_step(stmt); int step_res = sqlite3_step(stmt);
if (step_res != SQLITE_DONE) { if (step_res != SQLITE_DONE) {
fprintf(stderr, "error: %s\n", sqlite3_errmsg(connection)); fprintf(stderr, "error: %s\n", sqlite3_errmsg(connection));
@ -120,12 +170,17 @@ DbRes db_user_with_id(Db* db, User* user, int64_t id)
DbRes res; DbRes res;
sqlite3_stmt* stmt; sqlite3_stmt* stmt;
sqlite3_prepare_v2(connection, int prepare_res = sqlite3_prepare_v2(connection,
"SELECT id, name, email, password_hash, balance_dkk_cent" "SELECT id, name, email, password_hash, balance_dkk_cent"
" FROM users WHERE id = ?", " FROM users WHERE id = ?",
-1, -1,
&stmt, &stmt,
NULL); NULL);
if (prepare_res != SQLITE_OK) {
REPORT_SQLITE3_ERROR();
res = DbRes_Error;
goto l0_return;
}
sqlite3_bind_int64(stmt, 1, id); sqlite3_bind_int64(stmt, 1, id);
int step_res = sqlite3_step(stmt); int step_res = sqlite3_step(stmt);
@ -526,6 +581,7 @@ DbRes db_receipt_with_id(Db* db, Receipt* receipt, int64_t id)
receipt_product_vec_construct(&receipt->products); receipt_product_vec_construct(&receipt->products);
sqlite3_finalize(stmt);
prepare_res = sqlite3_prepare_v2(connection, prepare_res = sqlite3_prepare_v2(connection,
"SELECT id, receipt, product_price, amount FROM receipt_products" "SELECT id, receipt, product_price, amount FROM receipt_products"
" WHERE receipt = ?", " WHERE receipt = ?",

View File

@ -41,9 +41,12 @@ int main(void)
http_server_post(server, "/api/carts/purchase", route_post_carts_purchase); http_server_post(server, "/api/carts/purchase", route_post_carts_purchase);
http_server_get(server, "/api/receipts/one", route_get_receipt); http_server_get(server, "/api/receipts/one", route_get_receipts_one);
http_server_post(server, "/api/users/register", route_post_users_register); http_server_post(server, "/api/users/register", route_post_users_register);
http_server_post(
server, "/api/users/balance/add", route_post_users_balance_add);
http_server_post(server, "/api/sessions/login", route_post_sessions_login); http_server_post(server, "/api/sessions/login", route_post_sessions_login);
http_server_post( http_server_post(
server, "/api/sessions/logout", route_post_sessions_logout); server, "/api/sessions/logout", route_post_sessions_logout);

View File

@ -14,7 +14,7 @@ char* str_dup(const char* str)
return clone; return clone;
} }
const char* str_slice_copy(const StrSlice* slice) char* str_slice_copy(const StrSlice* slice)
{ {
char* copy = malloc(slice->len + 1); char* copy = malloc(slice->len + 1);
strncpy(copy, slice->ptr, slice->len); strncpy(copy, slice->ptr, slice->len);

View File

@ -12,7 +12,7 @@ typedef struct {
size_t len; size_t len;
} StrSlice; } StrSlice;
const char* str_slice_copy(const StrSlice* slice); char* str_slice_copy(const StrSlice* slice);
typedef struct { typedef struct {
const char* text; const char* text;

View File

@ -49,7 +49,18 @@ Deno.test("test backend", async (t) => {
// console.log(sessionUserRes.user); // console.log(sessionUserRes.user);
}); });
await testCartsAndReceipts(t, token); await t.step("test /api/users/balance/add", async () => {
const sessionUserRes = await post<{ ok: boolean }>(
"/api/users/balance/add",
{},
{ "Session-Token": token! },
);
// console.log(sessionUserRes);
assertEquals(sessionUserRes.ok, true);
});
await testCartsAndReceipts(t, token!);
await t.step("test /api/sessions/logout", async () => { await t.step("test /api/sessions/logout", async () => {
const logoutRes = await post<{ ok: boolean }>( const logoutRes = await post<{ ok: boolean }>(
@ -91,7 +102,7 @@ async function testCartsAndReceipts(t: Deno.TestContext, token: string) {
{ "Session-Token": token }, { "Session-Token": token },
); );
console.log(res); // console.log(res);
assertEquals(res.ok, true); assertEquals(res.ok, true);
}); });
} }