feat: finish back-end work (untested)

This commit is contained in:
2025-11-12 12:58:21 -07:00
parent b6a08573f3
commit 8b351524b8
18 changed files with 300 additions and 27 deletions

View File

@@ -156,8 +156,7 @@ func IsAuthenticated(auth *auth.Authenticator) gin.HandlerFunc {
context.Next()
} else {
if !HandleRefreshToken(session) {
context.String(http.StatusUnauthorized, "Failed to refresh access token")
context.Abort()
context.Redirect(http.StatusSeeOther, "/auth/login")
return
} else {
context.Next()

View File

@@ -33,6 +33,8 @@ func UserInDatabase(pool *pgxpool.Pool) gin.HandlerFunc {
return
}
log.Printf("%d", count)
if count == 0 {
_, exec_err := conn.Exec(context.Background(), queries.USER_CREATE_QUERY, sub_id, nickname)

View File

@@ -217,6 +217,14 @@ func AddItemToOrder(pool *pgxpool.Pool) gin.HandlerFunc {
return
}
_, update_order_filled_exec_err := conn.Exec(context.Background(), queries.SET_ORDER_FILLED, false, order_id)
if update_order_filled_exec_err != nil {
ctx.String(http.StatusInternalServerError, "AddItemToOrder(): ERROR... failed to set order state")
log.Printf("AddItemToOrder(): ERROR... failed to set order state... %s", update_order_filled_exec_err.Error())
return
}
var order_item_price dto.OrderItemPriceResponse
price_data_row := conn.QueryRow(context.Background(), queries.GET_ORDER_ITEM_PRICE, item_id, order_id)
@@ -281,3 +289,150 @@ func GetOrderItems(pool *pgxpool.Pool) gin.HandlerFunc {
ctx.JSON(http.StatusOK, items)
}
}
func SetItemMade(pool *pgxpool.Pool) gin.HandlerFunc {
return func(ctx *gin.Context) {
conn, conn_err := pool.Acquire(ctx)
if conn_err != nil {
log.Printf("SetItemMade(): ERROR: Failed to connect... %s", conn_err.Error())
ctx.AbortWithStatus(http.StatusInternalServerError)
return
}
defer conn.Release()
var request dto.ItemOrderSetMadeRequest
request_read_err := ctx.ShouldBindJSON(&request)
if request_read_err != nil {
ctx.String(http.StatusBadRequest, "SetItemMade(): ERROR... Error Request in bad format")
return
}
_, exec_set_made_err := conn.Exec(context.Background(), queries.SET_MADE, request.Made, request.ItemId, request.OrderId)
if exec_set_made_err != nil {
ctx.String(http.StatusInternalServerError, "SetItemMade(): ERROR... Internal server error")
log.Printf("SetItemMade(): ERROR... Failed to set item made... %s", exec_set_made_err.Error())
return
}
_, exec_update_order_err := conn.Exec(context.Background(), queries.UPDATE_ORDER_FILLED, request.OrderId)
if exec_update_order_err != nil {
ctx.String(http.StatusInternalServerError, "SetItemMade(): ERROR... Failed to update order")
log.Printf("SetItemMade(): ERROR... Failed to update order... %s", exec_update_order_err.Error())
return
}
var order_filled dto.OrderFilledResponse
scan_err := conn.QueryRow(context.Background(), queries.GET_ORDER_MADE_DETAILS, request.OrderId).Scan(&order_filled.OrderId, &order_filled.Filled)
if scan_err != nil {
if scan_err == pgx.ErrNoRows {
ctx.String(http.StatusBadRequest, "SetItemMade(): Invalid data")
return
}
ctx.String(http.StatusInternalServerError, "SetItemMade(): ERROR... Error querying result")
log.Printf("SetItemMade(): ERROR... Error querying result... %s", scan_err.Error())
return
}
ctx.JSON(http.StatusOK, order_filled)
}
}
func SetItemQuantity(pool *pgxpool.Pool) gin.HandlerFunc {
return func(ctx *gin.Context) {
conn, conn_err := pool.Acquire(ctx)
if conn_err != nil {
log.Printf("SetItemMade(): ERROR: Failed to connect... %s", conn_err.Error())
ctx.AbortWithStatus(http.StatusInternalServerError)
return
}
defer conn.Release()
var request dto.ItemOrderSetQuantityRequest
request_read_err := ctx.ShouldBindJSON(&request)
if request_read_err != nil {
ctx.String(http.StatusBadRequest, "SetItemQuantity(): ERROR... Error Request in bad format")
return
}
_, exec_set_quantity_err := conn.Exec(context.Background(), queries.SET_QUANTITY, request.Quantity, request.ItemId, request.OrderId)
if exec_set_quantity_err != nil {
ctx.String(http.StatusInternalServerError, "SetItemMade(): ERROR... Internal server error")
log.Printf("SetItemQuantity(): ERROR... Failed to set item quantity... %s", exec_set_quantity_err.Error())
return
}
_, exec_update_order_err := conn.Exec(context.Background(), queries.UPDATE_ORDER_FILLED, request.OrderId)
if exec_update_order_err != nil {
ctx.String(http.StatusInternalServerError, "SetItemMade(): ERROR... Failed to update order")
log.Printf("SetItemQuantity(): ERROR... Failed to update order... %s", exec_update_order_err.Error())
return
}
var order_filled dto.OrderFilledResponse
scan_err := conn.QueryRow(context.Background(), queries.GET_ORDER_MADE_DETAILS, request.OrderId).Scan(&order_filled.OrderId, &order_filled.Filled)
if scan_err != nil {
if scan_err == pgx.ErrNoRows {
ctx.String(http.StatusBadRequest, "SetItemQuantity(): Invalid data")
return
}
ctx.String(http.StatusInternalServerError, "SetItemQuantity(): ERROR... Error querying result")
log.Printf("SetItemQuantity(): ERROR... Error querying result... %s", scan_err.Error())
return
}
ctx.JSON(http.StatusOK, order_filled)
}
}
func DeleteOrderItem(pool *pgxpool.Pool) gin.HandlerFunc {
return func(ctx *gin.Context) {
conn, conn_err := pool.Acquire(ctx)
if conn_err != nil {
log.Printf("DeleteOrderItem(): ERROR: Failed to connect... %s", conn_err.Error())
ctx.AbortWithStatus(http.StatusInternalServerError)
return
}
defer conn.Release()
var request dto.DeleteOrderItemRequest
request_read_err := ctx.ShouldBindJSON(&request)
if request_read_err != nil {
ctx.String(http.StatusBadRequest, "DeleteOrderItem(): ERROR... invalid request")
return
}
_, exec_err := conn.Exec(context.Background(), queries.REMOVE_ITEM_FROM_ORDER, request.ItemId, request.OrderId)
if exec_err != nil {
ctx.String(http.StatusInternalServerError, "DeleteOrderItem(): ERROR.. Failed to delete item")
log.Printf("DeleteOrderItem(): ERROR... Failed to delte item... %s", exec_err.Error())
return
}
}
}
func DeleteItem(pool *pgxpool.Pool) gin.HandlerFunc {
return func(ctx *gin.Context) {
conn, conn_err := pool.Acquire(ctx)
if conn_err != nil {
log.Printf("DeleteItem(): ERROR: Failed to connect... %s", conn_err.Error())
ctx.AbortWithStatus(http.StatusInternalServerError)
return
}
defer conn.Release()
item_id := ctx.Query("item_id")
item_id_int, parse_err := strconv.ParseInt(item_id, 10, 64)
if parse_err != nil || item_id == "" {
ctx.String(http.StatusBadRequest, "DeleteItem(): Invalid data entry")
return
}
_, exec_err := conn.Exec(context.Background(), "DELETE FROM item WHERE id = $1", item_id_int)
if exec_err != nil {
ctx.String(http.StatusInternalServerError, "DeleteItem(): Failed to delete item")
log.Printf("DeleteItem(): ERROR... Failed to delete item... %s", exec_err.Error())
return
}
}
}

View File

@@ -31,7 +31,7 @@ func CreateOrder(pool *pgxpool.Pool) gin.HandlerFunc {
var current_user dto.UserResponse
user_query_err := conn.QueryRow(context.Background(), queries.GET_CURRENT_USER_OBJECT, sub_id).Scan(&current_user.Id, &current_user.Name, &current_user.Job_Position, &current_user.Active, &current_user.Admin)
user_query_err := conn.QueryRow(context.Background(), queries.GET_CURRENT_USER_OBJECT, sub_id).Scan(&current_user.Id, &current_user.Name, &current_user.JobPosition, &current_user.Active, &current_user.Admin)
if user_query_err != nil {
log.Printf("CreateOrder(): ERROR Failed to query user... %s", user_query_err.Error())
@@ -139,6 +139,14 @@ func GetOrderTable(pool *pgxpool.Pool) gin.HandlerFunc {
return
}
var table_query dto.OrderTableQuery
bind_err := ctx.ShouldBindJSON(&table_query)
if bind_err != nil {
ctx.String(http.StatusBadRequest, "GetOrderTable(): ERROR... invalid query")
return
}
filter := ctx.Query("filter")
filter_int, filter_parse_err := strconv.ParseInt(filter, 10, 64)
if filter == "" || filter_parse_err != nil {
@@ -147,7 +155,7 @@ func GetOrderTable(pool *pgxpool.Pool) gin.HandlerFunc {
}
query_string := utils.GetOrderTableQueryString(filter_int)
rows, query_err := conn.Query(context.Background(), query_string, utils.PAGE_SIZE*page_int, utils.PAGE_SIZE)
rows, query_err := conn.Query(context.Background(), query_string, utils.PAGE_SIZE*page_int, utils.PAGE_SIZE, table_query.Orderer, table_query.DateDue, table_query.DatePlaced)
if query_err != nil {
ctx.String(http.StatusInternalServerError, "GetOrdertable(): ERROR... internal server error")
log.Printf("GetOrderTable(): ERROR... Failed to query table...\nQUERY: %s\n %s", query_string, query_err.Error())
@@ -172,8 +180,28 @@ func GetOrderTable(pool *pgxpool.Pool) gin.HandlerFunc {
}
}
func SetOrderFilled(pool *pgxpool.Pool) gin.HandlerFunc {
func DeleteOrder(pool *pgxpool.Pool) gin.HandlerFunc {
return func(ctx *gin.Context) {
conn, conn_err := pool.Acquire(ctx)
if conn_err != nil {
log.Printf("DeleteOrder(): ERROR: Failed to connect... %s", conn_err.Error())
ctx.AbortWithStatus(http.StatusInternalServerError)
return
}
defer conn.Release()
order_id := ctx.Query("order_id")
order_id_int, parse_err := strconv.ParseInt(order_id, 10, 64)
if parse_err != nil {
ctx.String(http.StatusBadRequest, "DeleteOrder(): Invalid input")
return
}
_, exec_err := conn.Exec(context.Background(), "DELETE FROM order_record WHERE id = $1", order_id_int)
if exec_err != nil {
ctx.String(http.StatusInternalServerError, "DeleteOrder(): ERROR... failed to delete order")
log.Printf("DeleteOrder(): ERROR... Failed to delete order %s", exec_err.Error())
return
}
}
}

View File

@@ -21,6 +21,7 @@ func SetUserName(pool *pgxpool.Pool) gin.HandlerFunc {
if err != nil {
// TODO: Log this error
ctx.String(http.StatusInternalServerError, err.Error())
log.Printf("SetUserName(): ERROR... Failed to acquire connection... %s", err.Error())
return
}
user_profile, _ := ctx.Get("user_profile")
@@ -109,6 +110,13 @@ func GetUserTable(pool *pgxpool.Pool) gin.HandlerFunc {
}
defer conn.Release()
var request dto.UserRequest
body_read_err := ctx.ShouldBindJSON(&request)
if body_read_err != nil {
ctx.String(http.StatusBadRequest, "GetUserTable(): ERROR... Invalid user query object")
return
}
page := ctx.Query("page")
if page == "" {
ctx.String(http.StatusBadRequest, "GetUserTable(): Missing page")
@@ -119,7 +127,7 @@ func GetUserTable(pool *pgxpool.Pool) gin.HandlerFunc {
ctx.String(http.StatusBadRequest, "GetUserTable(): Not an integer")
return
}
rows, query_err := conn.Query(context.Background(), queries.USER_GET_TABLE_DATA, page_int*utils.PAGE_SIZE, utils.PAGE_SIZE)
rows, query_err := conn.Query(context.Background(), queries.USER_GET_TABLE_DATA, page_int*utils.PAGE_SIZE, utils.PAGE_SIZE, request.Name, request.JobPosition)
if query_err != nil {
ctx.String(http.StatusInternalServerError, "GetUserTable(): Failed to query database...")
log.Printf("GetUserTable(): ERROR... %s", query_err.Error())
@@ -129,7 +137,7 @@ func GetUserTable(pool *pgxpool.Pool) gin.HandlerFunc {
var users []dto.UserResponse
for rows.Next() {
var user dto.UserResponse
scan_err := rows.Scan(&user.Id, &user.Name, &user.Job_Position, &user.Active, &user.Admin)
scan_err := rows.Scan(&user.Id, &user.Name, &user.JobPosition, &user.Active, &user.Admin)
if scan_err != nil {
ctx.String(http.StatusInternalServerError, "GetUserTable(): ERROR: Failed to scan..")
log.Printf("GetUserTable(): ERROR... %s", scan_err.Error())
@@ -158,7 +166,7 @@ func GetCurrentAuthenticatedUser(pool *pgxpool.Pool) gin.HandlerFunc {
var user dto.UserResponse
query_err := conn.QueryRow(context.Background(), queries.GET_CURRENT_USER_OBJECT, sub_id).Scan(&user.Id, &user.Name, &user.Job_Position, &user.Active, &user.Admin)
query_err := conn.QueryRow(context.Background(), queries.GET_CURRENT_USER_OBJECT, sub_id).Scan(&user.Id, &user.Name, &user.JobPosition, &user.Active, &user.Admin)
if query_err != nil {
ctx.String(http.StatusInternalServerError, "GetCurrentAuthenticatedUser(): ERROR.... Failed to query")
log.Printf("GetCurrentAuthenticatedUser(): ERROR in querying user table... %s", query_err.Error())

View File

@@ -0,0 +1,6 @@
package dto
type DeleteOrderItemRequest struct {
OrderId int `json:"order_id"`
ItemId int `json:"item_id"`
}

View File

@@ -0,0 +1,7 @@
package dto
type ItemOrderSetMadeRequest struct {
OrderId int `json:"order_id"`
ItemId int `json:"item_id"`
Made int `json:"made"`
}

View File

@@ -0,0 +1,7 @@
package dto
type ItemOrderSetQuantityRequest struct {
OrderId int `json:"order_id"`
ItemId int `json:"item_id"`
Quantity int `json:"quantity"`
}

View File

@@ -0,0 +1,6 @@
package dto
type OrderFilledResponse struct {
OrderId int `json:"order_id"`
Filled bool `json:"filled"`
}

View File

@@ -0,0 +1,7 @@
package dto
type OrderTableQuery struct {
Orderer string `json:"orderer"`
DateDue string `json:"date_due"`
DatePlaced string `json:"date_placed"`
}

6
api/dto/user_request.go Normal file
View File

@@ -0,0 +1,6 @@
package dto
type UserRequest struct {
Name string `json:"name"`
JobPosition string `json:"job_position"`
}

View File

@@ -3,9 +3,9 @@ package dto
// User response for exposing to the front-end
// :3
type UserResponse struct {
Id int
Name string
Job_Position string
Active bool
Admin bool
Id int `json: "id"`
Name string `json: "name"`
JobPosition string `json: "job_position"`
Active bool `json: "active"`
Admin bool `json: "admin"`
}

View File

@@ -84,8 +84,13 @@ func main() {
router.PUT("/user/position", user_authenticated, middleware.GetUserProfile, user_in_db, user_active, user_is_admin, controllers.SetUserPosition(pool))
router.PUT("/item/price", user_authenticated, middleware.GetUserProfile, user_in_db, user_active, user_is_admin, controllers.SetItemPrice(pool))
router.PUT("/order/item", user_authenticated, middleware.GetUserProfile, user_in_db, user_active, controllers.AddItemToOrder(pool))
router.PUT("/item/made", user_authenticated, middleware.GetUserProfile, user_in_db, user_active, controllers.SetItemMade(pool))
router.PUT("/item/quantity", user_authenticated, middleware.GetUserProfile, user_in_db, user_active, controllers.SetItemQuantity(pool))
router.DELETE("/user/deactivate", middleware.IsAuthenticated(authenticator), middleware.GetUserProfile, user_in_db, user_active, user_is_admin, controllers.DeactivateUser(pool))
router.DELETE("/order/item", user_authenticated, middleware.GetUserProfile, user_in_db, user_active, controllers.DeleteOrderItem(pool))
router.DELETE("/order", user_authenticated, middleware.GetUserProfile, user_in_db, user_active, controllers.DeleteOrder(pool))
router.DELETE("/item", user_authenticated, middleware.GetUserProfile, user_in_db, user_active, user_is_admin, controllers.DeleteItem(pool))
router.Run("localhost:8080")
}

View File

@@ -85,3 +85,13 @@ DELETE FROM order_item WHERE item_id = $1 AND order_id = $2;
const SET_ITEM_IS_IN_SEASON = `
UPDATE item SET in_season = $1 WHERE id = $2;
`
const GET_ORDER_MADE_DETAILS = `
SELECT
id,
filled
FROM
order_record
WHERE
id = $1;
`

View File

@@ -61,7 +61,7 @@ WITH order_bill AS (
WHERE
orec.user_id = $1
AND orec.orderer = $2
AND orec.date_place = $3
AND orec.date_placed = $3
GROUP BY orec.id
) totals ON orec.id = totals.id
)
@@ -163,3 +163,19 @@ ORDER BY date_due DESC
OFFSET $1
LIMIT $2;
`
const UPDATE_ORDER_FILLED = `UPDATE order_record
SET filled = (
SELECT order_filled
FROM order_record orec
INNER JOIN (SELECT
orec.id,
COALESCE(EVERY(made = quantity), false) AS order_filled
FROM
order_record orec
LEFT JOIN order_item oi ON oi.order_id = orec.id
GROUP BY
orec.id) orec_result ON orec.id = orec_result.id
AND orec.id = $1
) WHERE order_record.id = $1
`

View File

@@ -31,6 +31,9 @@ FROM
ordr_user
INNER JOIN ordr_position
ON job_position = ordr_position.id
WHERE
user_name LIKE '%' || $3 ||'%'
AND position_name LIKE '%' || $4 || '%'
ORDER BY user_name
OFFSET $1
LIMIT $2;
@@ -45,7 +48,7 @@ SELECT
is_admin::boolean
FROM
ordr_user
INNER JOIN ordr_position
LEFT JOIN ordr_position
ON job_position = ordr_position.id
AND ordr_user.sub_id = $1;
`

View File

@@ -64,11 +64,15 @@ func GetOrderTableQueryString(filter int64) string {
ORDER BY date_due ` + order + `
OFFSET $1
LIMIT $2;
`
filter = filter & ^ASCEND_DATE_DUE
conditions := ""
conditions := `
WHERE
orderer LIKE '%' || $3 || '%'
AND TO_CHAR(date_due, 'YYYY-MM-DD') LIKE '%' || $4 || '%'
AND TO_CHAR(date_placed, 'YYYY-MM-DD') LIKE '%' || $5 || '%'
`
if filter > 0 {
if filter&FUTURE_FILTER > 0 {
conditions += "AND date_due > now()\n"
@@ -94,9 +98,6 @@ func GetOrderTableQueryString(filter int64) string {
if filter&UNPAID_FILTER > 0 {
conditions += "AND paid = FALSE\n"
}
if len(conditions) > 0 {
conditions = "WHERE " + conditions[4:]
}
}
query_string := query_string_first_part + conditions + query_string_second_part

View File

@@ -1,8 +1,15 @@
CREATE TABLE IF NOT EXISTS ordr_position (
id SERIAL NOT NULL PRIMARY KEY,
position_name TEXT NOT NULL
);
INSERT INTO ordr_position VALUES (0, 'Unassigned');
CREATE TABLE IF NOT EXISTS ordr_user (
id SERIAL NOT NULL PRIMARY KEY,
sub_id TEXT UNIQUE NOT NULL,
user_name TEXT NOT NULL,
position TEXT DEFAULT "",
position INTEGER NOT NULL DEFAULT 0 REFERENCES ordr_position ON DELETE SET NULL,
active BOOLEAN NOT NULL DEFAULT TRUE,
is_admin BOOLEAN NOT NULL DEFAULT FALSE
);
@@ -25,7 +32,7 @@ CREATE TABLE IF NOT EXISTS item (
);
CREATE TABLE IF NOT EXISTS item_price_history (
item_id INTEGER NOT NULL REFERENCES item(id),
item_id INTEGER NOT NULL REFERENCES item(id) ON DELETE CASCADE,
price REAL NOT NULL,
valid_from TIMESTAMP NOT NULL DEFAULT now(),
valid_to TIMESTAMP,
@@ -33,8 +40,8 @@ CREATE TABLE IF NOT EXISTS item_price_history (
);
CREATE TABLE IF NOT EXISTS order_item (
item_id INTEGER NOT NULL REFERENCES item(id),
order_id INTEGER NOT NULL REFERENCES order_record(id),
item_id INTEGER NOT NULL REFERENCES item(id) ON DELETE CASCADE,
order_id INTEGER NOT NULL REFERENCES order_record(id) ON DELETE CASCADE,
quantity INTEGER NOT NULL,
made INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT now(),
@@ -48,6 +55,6 @@ CREATE TABLE IF NOT EXISTS label (
);
CREATE TABLE IF NOT EXISTS order_label (
order_id INTEGER NOT NULL REFERENCES order_record(id),
label_id INTEGER NOT NULL REFERENCES label(id)
order_id INTEGER NOT NULL REFERENCES order_record(id) ON DELETE CASCADE,
label_id INTEGER NOT NULL REFERENCES label(id) ON DELETE CASCADE
);