get all products

This commit is contained in:
SimonFJ20 2025-03-05 15:45:49 +01:00
parent fa870b6988
commit 2ce894d756
9 changed files with 311 additions and 104 deletions

View File

@ -1,49 +1,54 @@
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY,
id INT PRIMARY KEY,
email TEXT NOT NULL,
password_hash TEXT NOT NULL,
balance_dkk_cent INTEGER NOT NULL
balance_dkk_cent INT NOT NULL
);
CREATE TABLE IF NOT EXISTS coords (
id INTEGER PRIMARY KEY,
x INTEGER NOT NULL,
y INTEGER NOT NULL
id INT PRIMARY KEY,
x INT NOT NULL,
y INT NOT NULL
);
CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY,
id INT PRIMARY KEY,
name TEXT NOT NULL,
price_dkk_cent INTEGER NOT NULL,
price_dkk_cent INT NOT NULL,
description TEXT NOT NULL,
coord INTEGER,
coord INT,
barcode TEXT,
FOREIGN KEY(coord) REFERENCES coords(id)
);
CREATE TABLE IF NOT EXISTS product_prices (
id INTEGER PRIMARY KEY,
product INTEGER NOT NULL,
price_dkk_cent INTEGER NOT NULL,
id INT PRIMARY KEY,
product INT NOT NULL,
price_dkk_cent INT NOT NULL,
FOREIGN KEY(product) REFERENCES products(id)
);
CREATE TABLE IF NOT EXISTS carts (
id INTEGER PRIMARY KEY,
user INTEGER NOT NULL,
id INT PRIMARY KEY,
user INT NOT NULL,
FOREIGN KEY(user) REFERENCES users(id)
);
CREATE TABLE IF NOT EXISTS cart_items (
id INTEGER PRIMARY KEY,
cart INTEGER NOT NULL,
amount INTEGER NOT NULL,
id INT PRIMARY KEY,
cart INT NOT NULL,
amount INT NOT NULL,
FOREIGN KEY(cart) REFERENCES carts(id)
);
INSERT OR REPLACE INTO users VALUES(1,'test@email.com','d980840fcb82970ab86656feebdccdd288be0e9b05f14e712b59529a2868fee3d980840fcb82970ab86656feebdccdd288be0e9b05f14e712b59529a2868fee3',10000);
INSERT OR REPLACE INTO products VALUES(1,'Letmælk',1195,'Mælk fra ko',NULL,NULL);
INSERT OR REPLACE INTO products VALUES(2,'Smør',2000,'Smør fra mejeri',NULL,NULL);

30
backend/src/controllers.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include "db.h"
#include <stdio.h>
#include <string.h>
typedef struct {
int number;
Db* db;
} Cx;
#define RESPOND(HTTP_CTX, STATUS, MIME_TYPE, ...) \
{ \
HttpCtx* _ctx = (HTTP_CTX); \
char _body[512]; \
snprintf(_body, 512 - 1, __VA_ARGS__); \
\
char content_length[24] = { 0 }; \
snprintf(content_length, 24 - 1, "%ld", strlen(_body)); \
\
http_ctx_res_headers_set(_ctx, "Content-Type", MIME_TYPE); \
http_ctx_res_headers_set(_ctx, "Content-Length", content_length); \
\
http_ctx_respond(_ctx, (STATUS), _body); \
}
#define RESPOND_HTML(HTTP_CTX, STATUS, ...) \
RESPOND(HTTP_CTX, STATUS, "text/html", __VA_ARGS__)
#define RESPOND_JSON(HTTP_CTX, STATUS, ...) \
RESPOND(HTTP_CTX, STATUS, "application/json", __VA_ARGS__)

View File

@ -0,0 +1,37 @@
#include "products.h"
#include "../controllers.h"
#include "../http_server.h"
#include "../models_json.h"
#include "../str_util.h"
void route_get_products_all(HttpCtx* ctx)
{
Cx* cx = http_ctx_user_ctx(ctx);
ProductVec products;
product_vec_construct(&products);
DbRes db_res = db_product_all_fill(cx->db, &products);
if (db_res != DbRes_Ok) {
RESPOND_JSON(ctx, 500, "{\"ok\":false,\"msg\":\"db error\"}");
return;
}
String res;
string_construct(&res);
string_push_str(&res, "{\"ok\":true,\"products\":[");
for (size_t i = 0; i < products.size; ++i) {
if (i != 0) {
string_push_str(&res, ",");
}
char* json = product_to_json_string(&products.data[i]);
string_push_str(&res, json);
free(json);
}
string_push_str(&res, "]}");
product_vec_destroy(&products);
RESPOND_JSON(ctx, 200, "%s", res.data);
string_destroy(&res);
}

View File

@ -0,0 +1,6 @@
#pragma once
#include "../controllers.h"
#include "../http_server.h"
void route_get_products_all(HttpCtx* ctx);

20
backend/src/db.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
#include "collection.h"
#include "models.h"
DEFINE_VEC(Product, ProductVec, product_vec, 32)
typedef enum {
DbRes_Ok,
DbRes_NotFound,
DbRes_Error,
} DbRes;
typedef struct Db Db;
/// `user.id` field is ignored.
DbRes db_user_insert(Db* db, User* user);
DbRes db_user_from_id(Db* db, User* user, int64_t id);
DbRes db_product_all_fill(Db* db, ProductVec* vec);

174
backend/src/db_sqlite.c Normal file
View File

@ -0,0 +1,174 @@
#include "db_sqlite.h"
#include "db.h"
#include "str_util.h"
#include <sqlite3.h>
#include <stdio.h>
static inline char* get_str_safe(sqlite3_stmt* stmt, int col)
{
const char* val = (const char*)sqlite3_column_text(stmt, col);
if (!val)
return str_dup("NULL");
return str_dup((const char*)val);
}
#define GET_INT(COL) sqlite3_column_int64(stmt, COL)
#define GET_STR(COL) get_str_safe(stmt, COL)
Db* db_sqlite_new(void)
{
Db* db = malloc(sizeof(Db));
sqlite3* connection;
int res = sqlite3_open("database.db", &connection);
if (res != SQLITE_OK) {
fprintf(stderr, "error: could not open sqlite 'database.db'\n");
return NULL;
}
sqlite3_close(connection);
return db;
}
void db_sqlite_free(Db* db)
{
// sqlite3_close(db->connection);
free(db);
}
static inline DbRes connect(sqlite3** connection)
{
int res = sqlite3_open("database.db", connection);
if (res != SQLITE_OK) {
fprintf(stderr, "error: could not open sqlite 'database.db'\n %s\n",
sqlite3_errmsg(*connection));
return DbRes_Error;
}
return DbRes_Ok;
}
static inline void disconnect(sqlite3* connection)
{
sqlite3_close(connection);
}
#define CONNECT \
{ \
if (connect(&connection) != DbRes_Ok) { \
return DbRes_Error; \
} \
}
#define DISCONNECT \
{ \
disconnect(connection); \
}
DbRes db_user_insert(Db* db, User* user)
{
sqlite3* connection;
CONNECT;
DbRes res;
sqlite3_stmt* stmt;
sqlite3_prepare_v2(connection,
"INSERT INTO users (email, password_hash, balance_dkk_cent) "
"VALUES (?, ?, ?)",
-1, &stmt, NULL);
sqlite3_bind_text(stmt, 1, user->email, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, user->password_hash, -1, SQLITE_STATIC);
sqlite3_bind_int64(stmt, 3, user->balance_dkk_cent);
int step_res = sqlite3_step(stmt);
if (step_res != SQLITE_DONE) {
fprintf(stderr, "error: %s\n", sqlite3_errmsg(connection));
res = DbRes_Error;
goto l0_return;
}
res = DbRes_Ok;
l0_return:
if (stmt)
sqlite3_finalize(stmt);
DISCONNECT;
return res;
}
DbRes db_user_from_id(Db* db, User* user, int64_t id)
{
sqlite3* connection;
CONNECT;
DbRes res;
sqlite3_stmt* stmt;
sqlite3_prepare_v2(connection,
"SELECT id, email, password_hash, balance_dkk_cent"
"FROM users WHERE id = ?",
-1, &stmt, NULL);
sqlite3_bind_int64(stmt, 1, id);
int step_res = sqlite3_step(stmt);
if (step_res == SQLITE_DONE) {
res = DbRes_NotFound;
goto l0_return;
} else if (step_res != SQLITE_ROW) {
fprintf(stderr, "error: %s\n", sqlite3_errmsg(connection));
res = DbRes_Error;
goto l0_return;
}
*user = (User) {
.id = GET_INT(0),
.email = GET_STR(1),
.password_hash = GET_STR(2),
.balance_dkk_cent = GET_INT(3),
};
res = DbRes_Ok;
l0_return:
if (stmt)
sqlite3_finalize(stmt);
DISCONNECT;
return res;
}
DbRes db_product_all_fill(Db* db, ProductVec* vec)
{
sqlite3* connection;
CONNECT;
DbRes res;
int sqlite_res;
sqlite3_stmt* stmt;
sqlite_res = sqlite3_prepare_v2(connection,
"SELECT id, name, price_dkk_cent, coord, barcode FROM products", -1,
&stmt, NULL);
if (sqlite_res != SQLITE_OK) {
fprintf(stderr, "error: %s\n", sqlite3_errmsg(connection));
res = DbRes_Error;
goto l0_return;
}
while ((sqlite_res = sqlite3_step(stmt)) == SQLITE_ROW) {
Product product = {
.id = GET_INT(0),
.name = GET_STR(1),
.price_dkk_cent = GET_INT(2),
.coord_id = GET_INT(3),
.barcode = GET_STR(4),
};
product_vec_push(vec, product);
}
if (sqlite_res != SQLITE_DONE) {
fprintf(stderr, "error: %s\n", sqlite3_errmsg(connection));
res = DbRes_Error;
goto l0_return;
}
res = DbRes_Ok;
l0_return:
if (stmt)
sqlite3_finalize(stmt);
DISCONNECT;
return res;
}

11
backend/src/db_sqlite.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include "db.h"
#include <sqlite3.h>
struct Db {
int empty;
};
Db* db_sqlite_new(void);
void db_sqlite_free(Db* db);

View File

@ -1,3 +1,6 @@
#include "controllers.h"
#include "controllers/products.h"
#include "db_sqlite.h"
#include "http_server.h"
#include "json.h"
#include "models.h"
@ -7,30 +10,6 @@
#include <stdio.h>
#include <string.h>
typedef struct {
int number;
} Cx;
#define RESPOND(HTTP_CTX, STATUS, MIME_TYPE, ...) \
{ \
HttpCtx* _ctx = (HTTP_CTX); \
char _body[512]; \
snprintf(_body, 512 - 1, __VA_ARGS__); \
\
char content_length[24] = { 0 }; \
snprintf(content_length, 24 - 1, "%ld", strlen(_body)); \
\
http_ctx_res_headers_set(_ctx, "Content-Type", MIME_TYPE); \
http_ctx_res_headers_set(_ctx, "Content-Length", content_length); \
\
http_ctx_respond(_ctx, (STATUS), _body); \
}
#define RESPOND_HTML(HTTP_CTX, STATUS, ...) \
RESPOND(HTTP_CTX, STATUS, "text/html", __VA_ARGS__)
#define RESPOND_JSON(HTTP_CTX, STATUS, ...) \
RESPOND(HTTP_CTX, STATUS, "application/json", __VA_ARGS__)
void route_get_index(HttpCtx* ctx)
{
Cx* cx = http_ctx_user_ctx(ctx);
@ -66,76 +45,17 @@ l0_return:
json_free(body);
}
static inline void insert_test_user(sqlite3* db)
{
sqlite3_stmt* stmt;
sqlite3_prepare_v2(db,
"INSERT INTO users (email, password_hash, balance_dkk_cent) "
"VALUES (?, ?, ?)",
-1, &stmt, NULL);
char email[] = "testuser@email.com";
char password[] = "1234";
StrHash password_hash = str_hash(password);
char* password_hash_str = str_hash_to_string(password_hash);
sqlite3_bind_text(stmt, 1, email, -1, SQLITE_STATIC);
sqlite3_bind_text(stmt, 2, password_hash_str, -1, SQLITE_STATIC);
sqlite3_bind_int64(stmt, 3, 123);
int res = sqlite3_step(stmt);
if (res != SQLITE_DONE) {
fprintf(stderr, "error: could not insert test user: %s\n",
sqlite3_errmsg(db));
}
sqlite3_finalize(stmt);
}
HttpServer* server;
int main(void)
{
Db* db = db_sqlite_new();
User user = {
.id = 12,
.email = str_dup("test@mail.dk"),
.password_hash = str_dup("hawd"),
.balance_dkk_cent = 321,
Cx cx = {
.number = 1,
.db = db,
};
char* str = user_to_json_string(&user);
printf("user = '%s'\n", str);
User user2;
JsonValue* json = json_parse(str, strlen(str));
user_from_json(&user2, json);
char* str2 = user_to_json_string(&user2);
printf("user2 = '%s'\n", str2);
user_free(&user);
user_free(&user2);
json_free(json);
free(str);
free(str2);
return 0;
sqlite3* db;
int res = sqlite3_open("database.db", &db);
if (res != SQLITE_OK) {
fprintf(stderr, "error: could not open sqlite 'database.db'\n");
return -1;
}
insert_test_user(db);
Cx cx = { .number = 1 };
server = http_server_new((HttpServerOpts) {
.port = 8080,
.workers_amount = 8,
@ -145,6 +65,9 @@ int main(void)
}
http_server_set_user_ctx(server, &cx);
http_server_get(server, "/products/all", route_get_products_all);
http_server_get(server, "/", route_get_index);
http_server_post(server, "/set_number", route_post_set_number);
@ -152,5 +75,5 @@ int main(void)
http_server_listen(server);
http_server_free(server);
sqlite3_close(db);
db_sqlite_free(db);
}

View File

@ -46,6 +46,7 @@ void string_push_str(String* string, const char* str)
for (size_t i = 0; i < strlen(str); ++i) {
string_push(string, str[i]);
}
string->data[string->size] = '\0';
}
void string_push_fmt_va(String* string, const char* fmt, ...)