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 ( CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY, id INT PRIMARY KEY,
email TEXT NOT NULL, email TEXT NOT NULL,
password_hash 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 ( CREATE TABLE IF NOT EXISTS coords (
id INTEGER PRIMARY KEY, id INT PRIMARY KEY,
x INTEGER NOT NULL, x INT NOT NULL,
y INTEGER NOT NULL y INT NOT NULL
); );
CREATE TABLE IF NOT EXISTS products ( CREATE TABLE IF NOT EXISTS products (
id INTEGER PRIMARY KEY, id INT PRIMARY KEY,
name TEXT NOT NULL, name TEXT NOT NULL,
price_dkk_cent INTEGER NOT NULL, price_dkk_cent INT NOT NULL,
description TEXT NOT NULL, description TEXT NOT NULL,
coord INTEGER, coord INT,
barcode TEXT, barcode TEXT,
FOREIGN KEY(coord) REFERENCES coords(id) FOREIGN KEY(coord) REFERENCES coords(id)
); );
CREATE TABLE IF NOT EXISTS product_prices ( CREATE TABLE IF NOT EXISTS product_prices (
id INTEGER PRIMARY KEY, id INT PRIMARY KEY,
product INTEGER NOT NULL, product INT NOT NULL,
price_dkk_cent INTEGER NOT NULL, price_dkk_cent INT NOT NULL,
FOREIGN KEY(product) REFERENCES products(id) FOREIGN KEY(product) REFERENCES products(id)
); );
CREATE TABLE IF NOT EXISTS carts ( CREATE TABLE IF NOT EXISTS carts (
id INTEGER PRIMARY KEY, id INT PRIMARY KEY,
user INTEGER NOT NULL, user INT NOT NULL,
FOREIGN KEY(user) REFERENCES users(id) FOREIGN KEY(user) REFERENCES users(id)
); );
CREATE TABLE IF NOT EXISTS cart_items ( CREATE TABLE IF NOT EXISTS cart_items (
id INTEGER PRIMARY KEY, id INT PRIMARY KEY,
cart INTEGER NOT NULL, cart INT NOT NULL,
amount INTEGER NOT NULL, amount INT NOT NULL,
FOREIGN KEY(cart) REFERENCES carts(id) 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 "http_server.h"
#include "json.h" #include "json.h"
#include "models.h" #include "models.h"
@ -7,30 +10,6 @@
#include <stdio.h> #include <stdio.h>
#include <string.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) void route_get_index(HttpCtx* ctx)
{ {
Cx* cx = http_ctx_user_ctx(ctx); Cx* cx = http_ctx_user_ctx(ctx);
@ -66,76 +45,17 @@ l0_return:
json_free(body); 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; HttpServer* server;
int main(void) int main(void)
{ {
Db* db = db_sqlite_new();
User user = { Cx cx = {
.id = 12, .number = 1,
.email = str_dup("test@mail.dk"), .db = db,
.password_hash = str_dup("hawd"),
.balance_dkk_cent = 321,
}; };
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) { server = http_server_new((HttpServerOpts) {
.port = 8080, .port = 8080,
.workers_amount = 8, .workers_amount = 8,
@ -145,6 +65,9 @@ int main(void)
} }
http_server_set_user_ctx(server, &cx); 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_get(server, "/", route_get_index);
http_server_post(server, "/set_number", route_post_set_number); http_server_post(server, "/set_number", route_post_set_number);
@ -152,5 +75,5 @@ int main(void)
http_server_listen(server); http_server_listen(server);
http_server_free(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) { for (size_t i = 0; i < strlen(str); ++i) {
string_push(string, str[i]); string_push(string, str[i]);
} }
string->data[string->size] = '\0';
} }
void string_push_fmt_va(String* string, const char* fmt, ...) void string_push_fmt_va(String* string, const char* fmt, ...)