postgres-conversion #3
|
|
@ -2,3 +2,5 @@ backend/target
|
|||
backend/db
|
||||
backend/id_rsa
|
||||
backend/id_rsa.pub
|
||||
backend/sqlite3.env
|
||||
backend/.env
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
DATABASE_URL=sqlite://db/db.sqlite3
|
||||
GOOGLE_CLIENT_ID=735264084619-clsmvgdqdmum4rvrcj0kuk28k9agir1c.apps.googleusercontent.com
|
||||
GOOGLE_CLIENT_SECRET=L6uI7FQGoMJd-ay1HO_iGJ6M
|
||||
SMTP_SERVER_NAME=mailout.easymail.ca
|
||||
SMTP_SERVER_PORT=587
|
||||
EMAIL_USERNAME=admin@jean-marie.ca
|
||||
EMAIL_PASSWORD=Cj6wX8^JivPD
|
||||
|
|
@ -2603,6 +2603,7 @@ dependencies = [
|
|||
"atoi",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc",
|
||||
"crossbeam-queue",
|
||||
"either",
|
||||
|
|
@ -2631,6 +2632,7 @@ dependencies = [
|
|||
"tokio-stream",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2683,6 +2685,7 @@ dependencies = [
|
|||
"bitflags 2.6.0",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"crc",
|
||||
"digest",
|
||||
"dotenvy",
|
||||
|
|
@ -2711,6 +2714,7 @@ dependencies = [
|
|||
"stringprep",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"uuid",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
|
|
@ -2724,6 +2728,7 @@ dependencies = [
|
|||
"base64 0.22.1",
|
||||
"bitflags 2.6.0",
|
||||
"byteorder",
|
||||
"chrono",
|
||||
"crc",
|
||||
"dotenvy",
|
||||
"etcetera",
|
||||
|
|
@ -2749,6 +2754,7 @@ dependencies = [
|
|||
"stringprep",
|
||||
"thiserror",
|
||||
"tracing",
|
||||
"uuid",
|
||||
"whoami",
|
||||
]
|
||||
|
||||
|
|
@ -2759,6 +2765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680"
|
||||
dependencies = [
|
||||
"atoi",
|
||||
"chrono",
|
||||
"flume",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
|
|
@ -2773,6 +2780,7 @@ dependencies = [
|
|||
"sqlx-core",
|
||||
"tracing",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ oauth2 = "4.4"
|
|||
http = "1.1"
|
||||
tower-http = { version = "0.6.1", features = ["full"] }
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio", "macros"] }
|
||||
sqlx = { version = "0.8", features = ["postgres", "sqlite","runtime-tokio", "macros", "chrono", "uuid"] }
|
||||
uuid = { version = "1.10", features = ["v4"] }
|
||||
dotenvy = "0.15"
|
||||
constant_time_eq = "0.3"
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
-- Add down migration script here
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
-- Add up migration script here
|
||||
CREATE TABLE "oauth2_state_storage" (
|
||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"csrf_state" text NOT NULL,
|
||||
"pkce_code_verifier" text NOT NULL,
|
||||
"return_url" text NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "user_sessions" (
|
||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"user_id" integer NOT NULL,
|
||||
"session_token_p1" text NOT NULL,
|
||||
"session_token_p2" text NOT NULL,
|
||||
"created_at" integer NOT NULL,
|
||||
"expires_at" integer NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE "users" (
|
||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"created_at" integer NOT NULL,
|
||||
"created_by" integer NOT NULL,
|
||||
"updated_at" integer NOT NULL,
|
||||
"updated_by" integer NOT NULL,
|
||||
"email" text NOT NULL UNIQUE,
|
||||
"name" text NOT NULL,
|
||||
"family_name" text NOT NULL,
|
||||
"given_name" text NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS roles (
|
||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"created_at" integer NOT NULL,
|
||||
"created_by" integer NOT NULL,
|
||||
"updated_at" integer NOT NULL,
|
||||
"updated_by" integer NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS user_roles (
|
||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"created_at" integer NOT NULL,
|
||||
"created_by" integer NOT NULL,
|
||||
"updated_at" integer NOT NULL,
|
||||
"updated_by" integer NOT NULL,
|
||||
"user_id" integer NOT NULL,
|
||||
"role_id" integer NOT NULL
|
||||
);
|
||||
|
||||
create TABLE IF NOT EXISTS role_permissions (
|
||||
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||
"created_at" integer NOT NULL,
|
||||
"created_by" integer NOT NULL,
|
||||
"updated_at" integer NOT NULL,
|
||||
"updated_by" integer NOT NULL,
|
||||
"role_id" integer NOT NULL,
|
||||
"item" text NOT NULL
|
||||
);
|
||||
|
|
@ -1 +0,0 @@
|
|||
-- Add down migration script here
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
-- Add up migration script here
|
||||
INSERT INTO "main"."roles" ("id", "created_at", "created_by", "updated_at", "updated_by", "name", "description") VALUES ('1', '0', '0', '0', '0', 'public', 'Users with only anonymous access');
|
||||
INSERT INTO "main"."roles" ("id", "created_at", "created_by", "updated_at", "updated_by", "name", "description") VALUES ('2', '0', '0', '0', '0', 'normal', 'Users with no elevated privileges');
|
||||
INSERT INTO "main"."roles" ("id", "created_at", "created_by", "updated_at", "updated_by", "name", "description") VALUES ('3', '0', '0', '0', '0', 'editor', 'Users with basic elevated privileges');
|
||||
INSERT INTO "main"."roles" ("id", "created_at", "created_by", "updated_at", "updated_by", "name", "description") VALUES ('4', '0', '0', '0', '0', 'admin', 'Users with full administrative privileges');
|
||||
|
||||
|
|
@ -1 +0,0 @@
|
|||
-- Add down migration script here
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
-- Add up migration script here
|
||||
-- Role permissions
|
||||
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('1', '0', '0', '0', '0', '1', '/');
|
||||
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('2', '0', '0', '0', '0', '1', '/login');
|
||||
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('3', '0', '0', '0', '0', '1', '/logout');
|
||||
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('4', '0', '0', '0', '0', '2', '/dashboard');
|
||||
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('5', '0', '0', '0', '0', '2', '/profile');
|
||||
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('6', '0', '0', '0', '0', '4', '/useradmin');
|
||||
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('7', '0', '0', '0', '0', '4', '/users');
|
||||
|
||||
-- First user is an admin
|
||||
INSERT INTO "main"."user_roles" ("id", "created_at", "created_by", "updated_at", "updated_by", "user_id", "role_id") VALUES ('2', '0', '0', '0', '0', '1', '4');
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
-- Add down migration script here
|
||||
-- Delete role records
|
||||
DELETE FROM "main"."roles" WHERE "id" = '5';
|
||||
|
||||
-- Delete permission records
|
||||
DELETE FROM "main"."role_permissions" WHERE "id" = '8';
|
||||
|
||||
-- Delete user role records
|
||||
DELETE FROM "main"."user_roles" WHERE "role_id" = '5';
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
-- Add up migration script here
|
||||
-- Add roles for calendar
|
||||
INSERT INTO "main"."roles" ("id", "created_at", "created_by", "updated_at", "updated_by", "name", "description") VALUES ('5', '0', '0', '0', '0', 'calendar', 'Users with access to the calendar');
|
||||
|
||||
-- Add permissions for calendar
|
||||
INSERT INTO "main"."role_permissions" ("id", "created_at", "created_by", "updated_at", "updated_by", "role_id", "item") VALUES ('8', '0', '0', '0', '0', '5', '/cottagecalendar');
|
||||
|
||||
-- Add user roles for calendar
|
||||
INSERT INTO "main"."user_roles" ("id", "created_at", "created_by", "updated_at", "updated_by", "user_id", "role_id") VALUES ('1', '0', '0', '0', '0', '1', '5');
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
-- Add down migration script here
|
||||
drop table if exists `wishlist_items`;
|
||||
|
||||
delete from `role_permissions` where id = 9;
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
-- Add up migration script here
|
||||
CREATE TABLE
|
||||
`wishlist_items` (
|
||||
`id` integer not null primary key autoincrement,
|
||||
`created_at` INTEGER not null default CURRENT_TIMESTAMP,
|
||||
`created_by` ineger null,
|
||||
`updated_at` INTEGER null default CURRENT_TIMESTAMP,
|
||||
`updated_by` integer null,
|
||||
`user_id` INTEGER null,
|
||||
`item` varchar(255) null,
|
||||
`item_url` varchar(255) null,
|
||||
`purchased_by` INTEGER null,
|
||||
unique (`id`)
|
||||
);
|
||||
|
||||
insert into `role_permissions` (`created_at`, `created_by`, `id`, `item`, `role_id`, `updated_at`, `updated_by`) values ('0', '0', '9', '/wishlist', '2', '0', '0')
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
-- Add down migration script here
|
||||
alter table wishlist_items drop column received_at;
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
-- Add up migration script here
|
||||
alter table wishlist_items add column received_at integer null;
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
-- Add down migration script here
|
||||
drop table gift_exchange;
|
||||
drop table gift_exchange_participants;
|
||||
|
||||
delete from role_permissions where item = '/giftexchange';
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
-- Add up migration script here
|
||||
CREATE TABLE
|
||||
`gift_exchange` (
|
||||
`id` integer not null primary key autoincrement,
|
||||
`created_at` INTEGER not null default CURRENT_TIMESTAMP,
|
||||
`created_by` integer not null default 0,
|
||||
`updated_at` INTEGER not null default CURRENT_TIMESTAMP,
|
||||
`updated_by` integer not null default 0,
|
||||
`name` varchar(255) not null,
|
||||
`exchange_date` INTEGER not null,
|
||||
`status` INTEGER not null default 0,
|
||||
unique (`id`)
|
||||
);
|
||||
|
||||
CREATE TABLE
|
||||
`gift_exchange_participants` (
|
||||
`id` integer not null primary key autoincrement,
|
||||
`created_at` INTEGER not null default CURRENT_TIMESTAMP,
|
||||
`created_by` integer not null default 0,
|
||||
`updated_at` INTEGER not null default CURRENT_TIMESTAMP,
|
||||
`updated_by` integer not null default 0,
|
||||
`exchange_id` INTEGER not null,
|
||||
`participant_id` INTEGER not null,
|
||||
`gifter_id` INTEGER not null,
|
||||
unique (`id`)
|
||||
);
|
||||
|
||||
insert into `role_permissions` (`created_at`, `created_by`, `id`, `item`, `role_id`, `updated_at`, `updated_by`) values ('0', '0', '10', '/giftexchange', '2', '0', '0')
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
-- Add down migration script here
|
||||
|
||||
drop table if exists `calendar_events`;
|
||||
drop table if exists `calendar_event_types`;
|
||||
drop table if exists `calendar`;
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
-- Add up migration script here
|
||||
|
||||
-- Calendars
|
||||
-- 1 - Cottage
|
||||
-- 2 - Family tree
|
||||
create table calendar (
|
||||
id integer not null primary key autoincrement,
|
||||
created_at integer not null default CURRENT_TIMESTAMP,
|
||||
created_by integer not null default 0,
|
||||
updated_at integer null default CURRENT_TIMESTAMP,
|
||||
updated_by integer not null default 0,
|
||||
name varchar(255) not null
|
||||
);
|
||||
|
||||
-- Event types
|
||||
-- 1 - Rental
|
||||
-- 2 - Life event
|
||||
create table calendar_event_types (
|
||||
id integer not null primary key autoincrement,
|
||||
created_at integer not null default CURRENT_TIMESTAMP,
|
||||
created_by integer not null default 0,
|
||||
updated_at integer null default CURRENT_TIMESTAMP,
|
||||
updated_by integer not null default 0,
|
||||
name varchar(255) not null
|
||||
);
|
||||
|
||||
create table calendar_events (
|
||||
id integer not null primary key autoincrement,
|
||||
created_at integer not null default CURRENT_TIMESTAMP,
|
||||
created_by integer not null default 0,
|
||||
updated_at integer null default CURRENT_TIMESTAMP,
|
||||
updated_by integer not null default 0,
|
||||
calendar_id integer not null,
|
||||
event_type_id integer not null,
|
||||
title varchar(255) not null,
|
||||
description varchar(255) null,
|
||||
start_time integer null,
|
||||
end_time integer null,
|
||||
repeat_type integer not null default 0, -- 0 - None, 1 - Daily, 2 - Weekly, 3 - Monthly, 4 - Yearly, 5 - Day of week, 6 - Day of month
|
||||
repeat_interval integer not null default 0,
|
||||
celebrate boolean not null default 0
|
||||
);
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
-- Drop Postgres tables
|
||||
drop table if exists oauth2_state_storage;
|
||||
drop table if exists user_sessions;
|
||||
drop table if exists users;
|
||||
drop table if exists roles;
|
||||
drop table if exists user_roles;
|
||||
drop table if exists role_permissions;
|
||||
drop table if exists wishlist_items;
|
||||
drop table if exists gift_exchange;
|
||||
drop table if exists gift_exchange_participants;
|
||||
drop table if exists calendar;
|
||||
drop table if exists calendar_event_types;
|
||||
drop table if exists calendar_events;
|
||||
|
|
@ -0,0 +1,386 @@
|
|||
-- Create Postgres tables
|
||||
create table if not exists oauth2_state_storage (
|
||||
id uuid PRIMARY KEY default gen_random_uuid(),
|
||||
csrf_state text NOT NULL,
|
||||
pkce_code_verifier text NOT NULL,
|
||||
return_url text NOT NULL
|
||||
);
|
||||
|
||||
create table if not exists user_sessions (
|
||||
id uuid PRIMARY KEY default gen_random_uuid(),
|
||||
user_id uuid NOT NULL,
|
||||
session_token_p1 text NOT NULL,
|
||||
session_token_p2 text NOT NULL,
|
||||
created_at timestamp NOT NULL,
|
||||
expires_at timestamp NOT NULL
|
||||
);
|
||||
|
||||
create table if not exists users (
|
||||
id uuid PRIMARY KEY default gen_random_uuid(),
|
||||
created_at timestamp NOT NULL default now(),
|
||||
created_by uuid NOT NULL,
|
||||
updated_at timestamp NOT NULL default now(),
|
||||
updated_by uuid NOT NULL,
|
||||
email text NOT NULL UNIQUE,
|
||||
"name" text NOT NULL,
|
||||
family_name text NOT NULL,
|
||||
given_name text NOT NULL
|
||||
);
|
||||
|
||||
create table IF NOT EXISTS roles (
|
||||
id uuid PRIMARY KEY default gen_random_uuid(),
|
||||
created_at timestamp NOT NULL default now(),
|
||||
created_by uuid NOT NULL,
|
||||
updated_at timestamp NOT NULL default now(),
|
||||
updated_by uuid NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT
|
||||
);
|
||||
|
||||
create table IF NOT EXISTS user_roles (
|
||||
id uuid PRIMARY KEY default gen_random_uuid(),
|
||||
created_at timestamp NOT NULL default now(),
|
||||
created_by uuid NOT NULL,
|
||||
updated_at timestamp NOT NULL default now(),
|
||||
updated_by uuid NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
role_id uuid NOT NULL
|
||||
);
|
||||
|
||||
create unique index if not exists unique_user_role on user_roles(user_id, role_id);
|
||||
|
||||
create table IF NOT EXISTS role_permissions (
|
||||
id uuid PRIMARY KEY default gen_random_uuid(),
|
||||
created_at timestamp NOT NULL default now(),
|
||||
created_by uuid NOT NULL,
|
||||
updated_at timestamp NOT NULL default now(),
|
||||
updated_by uuid NOT NULL,
|
||||
role_id uuid NOT NULL,
|
||||
item text NOT NULL
|
||||
);
|
||||
|
||||
create table if not exists wishlist_items (
|
||||
id uuid PRIMARY KEY default gen_random_uuid(),
|
||||
created_at timestamp not null default now(),
|
||||
created_by uuid null,
|
||||
updated_at timestamp null default now(),
|
||||
updated_by uuid null,
|
||||
user_id uuid null,
|
||||
item varchar(512) null,
|
||||
item_url varchar(1024) null,
|
||||
purchased_by uuid null,
|
||||
received_at timestamp null
|
||||
);
|
||||
|
||||
create table if not exists gift_exchange (
|
||||
id uuid PRIMARY KEY default gen_random_uuid(),
|
||||
created_at timestamp not null default now(),
|
||||
created_by uuid not null,
|
||||
updated_at timestamp not null default now(),
|
||||
updated_by uuid not null,
|
||||
"name" varchar(255) not null,
|
||||
exchange_date timestamp not null,
|
||||
"status" INTEGER not null default 0
|
||||
);
|
||||
|
||||
create table if not exists gift_exchange_participants (
|
||||
id uuid PRIMARY KEY default gen_random_uuid(),
|
||||
created_at timestamp not null default now(),
|
||||
created_by uuid not null,
|
||||
updated_at timestamp not null default now(),
|
||||
updated_by uuid not null,
|
||||
exchange_id uuid not null,
|
||||
participant_id uuid not null,
|
||||
gifter_id uuid not null
|
||||
);
|
||||
|
||||
-- Calendars
|
||||
-- 1 - Cottage
|
||||
-- 2 - Family tree
|
||||
create table if not exists calendar (
|
||||
id uuid PRIMARY KEY default gen_random_uuid(),
|
||||
created_at timestamp not null default now(),
|
||||
created_by uuid not null,
|
||||
updated_at timestamp null default now(),
|
||||
updated_by uuid not null,
|
||||
"name" varchar(255) not null
|
||||
);
|
||||
|
||||
-- Event types
|
||||
-- 1 - Rental
|
||||
-- 2 - Life event
|
||||
create table if not exists calendar_event_types (
|
||||
id uuid PRIMARY KEY default gen_random_uuid(),
|
||||
created_at timestamp not null default now(),
|
||||
created_by uuid not null,
|
||||
updated_at timestamp null default now(),
|
||||
updated_by uuid not null,
|
||||
"name" varchar(255) not null
|
||||
);
|
||||
|
||||
create table if not exists calendar_events (
|
||||
id uuid PRIMARY KEY default gen_random_uuid(),
|
||||
created_at timestamp not null default now(),
|
||||
created_by uuid not null,
|
||||
updated_at timestamp null default now(),
|
||||
updated_by uuid not null,
|
||||
calendar_id uuid not null,
|
||||
event_type_id uuid not null,
|
||||
title varchar(255) not null,
|
||||
"description" varchar(255) null,
|
||||
start_time timestamp null,
|
||||
end_time timestamp null,
|
||||
repeat_type integer not null default 0,
|
||||
-- 0 - None, 1 - Daily, 2 - Weekly, 3 - Monthly, 4 - Yearly, 5 - Day of week, 6 - Day of month
|
||||
repeat_interval integer not null default 0,
|
||||
celebrate boolean not null default true
|
||||
);
|
||||
|
||||
do $$
|
||||
|
||||
declare user_uuid uuid := gen_random_uuid();
|
||||
|
||||
begin -- Initial user
|
||||
insert into users (
|
||||
id,
|
||||
"name",
|
||||
created_by,
|
||||
updated_by,
|
||||
email,
|
||||
family_name,
|
||||
given_name
|
||||
)
|
||||
values
|
||||
(
|
||||
user_uuid,
|
||||
'admin',
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
'admin@jean-marie.ca',
|
||||
'',
|
||||
'admin'
|
||||
);
|
||||
|
||||
-- Initial roles
|
||||
INSERT INTO
|
||||
roles (created_by, updated_by, "name", "description")
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
'public',
|
||||
'Users with only anonymous access'
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
roles (created_by, updated_by, "name", "description")
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
'normal',
|
||||
'Users with no elevated privileges'
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
roles (created_by, updated_by, "name", "description")
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
'editor',
|
||||
'Users with basic elevated privileges'
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
roles (created_by, updated_by, "name", "description")
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
'admin',
|
||||
'Users with full administrative privileges'
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
roles (created_by, updated_by, "name", "description")
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
'calendar',
|
||||
'Users with access to the calendar'
|
||||
);
|
||||
|
||||
-- Initial permissions
|
||||
INSERT INTO
|
||||
role_permissions (created_by, updated_by, role_id, item)
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
(
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
roles
|
||||
WHERE
|
||||
"name" = 'public'
|
||||
),
|
||||
'/'
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
role_permissions (created_by, updated_by, role_id, item)
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
(
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
roles
|
||||
WHERE
|
||||
"name" = 'public'
|
||||
),
|
||||
'/login'
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
role_permissions (created_by, updated_by, role_id, item)
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
(
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
roles
|
||||
WHERE
|
||||
"name" = 'public'
|
||||
),
|
||||
'/logout'
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
role_permissions (created_by, updated_by, role_id, item)
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
(
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
roles
|
||||
WHERE
|
||||
"name" = 'normal'
|
||||
),
|
||||
'/dashboard'
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
role_permissions (created_by, updated_by, role_id, item)
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
(
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
roles
|
||||
WHERE
|
||||
"name" = 'normal'
|
||||
),
|
||||
'/profile'
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
role_permissions (created_by, updated_by, role_id, item)
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
(
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
roles
|
||||
WHERE
|
||||
"name" = 'admin'
|
||||
),
|
||||
'/useradmin'
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
role_permissions (created_by, updated_by, role_id, item)
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
(
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
roles
|
||||
WHERE
|
||||
"name" = 'admin'
|
||||
),
|
||||
'/users'
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
role_permissions (created_by, updated_by, role_id, item)
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
(
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
roles
|
||||
WHERE
|
||||
"name" = 'calendar'
|
||||
),
|
||||
'/calendar'
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
role_permissions (created_by, updated_by, role_id, item)
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
(
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
roles
|
||||
WHERE
|
||||
"name" = 'normal'
|
||||
),
|
||||
'/wishlist'
|
||||
);
|
||||
|
||||
INSERT INTO
|
||||
role_permissions (created_by, updated_by, role_id, item)
|
||||
VALUES
|
||||
(
|
||||
user_uuid,
|
||||
user_uuid,
|
||||
(
|
||||
SELECT
|
||||
id
|
||||
FROM
|
||||
roles
|
||||
WHERE
|
||||
"name" = 'normal'
|
||||
),
|
||||
'/giftexchange'
|
||||
);
|
||||
|
||||
end $$;
|
||||
|
|
@ -2,11 +2,11 @@ use askama::Template;
|
|||
use askama_axum::{IntoResponse, Response};
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{Html, Json, Redirect},
|
||||
response::{Html, Redirect},
|
||||
Extension,
|
||||
};
|
||||
use http::StatusCode;
|
||||
use sqlx::SqlitePool;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{
|
||||
middlewares::is_authorized,
|
||||
|
|
@ -47,7 +47,7 @@ struct CalendarTemplate {
|
|||
|
||||
pub async fn calendar(
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(db_pool): State<PgPool>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
let logged_in = user_data.is_some();
|
||||
|
|
@ -89,7 +89,7 @@ pub async fn calendar(
|
|||
|
||||
pub async fn get_events(
|
||||
Path(calendar): Path<String>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> String {
|
||||
println!("Calendar: {}", calendar);
|
||||
|
|
@ -101,8 +101,8 @@ pub async fn get_events(
|
|||
|
||||
if logged_in {
|
||||
// Extract the user data.
|
||||
let user = user_data.as_ref().unwrap().clone();
|
||||
let userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
|
||||
let _user = user_data.as_ref().unwrap().clone();
|
||||
let _userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
|
||||
|
||||
if is_authorized("/calendar", user_data, db_pool.clone()).await {
|
||||
// Get requested calendar events from database
|
||||
|
|
|
|||
|
|
@ -17,8 +17,7 @@ use oauth2::{
|
|||
TokenResponse, TokenUrl,
|
||||
};
|
||||
|
||||
use chrono::Utc;
|
||||
use sqlx::SqlitePool;
|
||||
use sqlx::PgPool;
|
||||
use std::collections::HashMap;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
|
@ -61,7 +60,7 @@ fn get_client(hostname: String) -> Result<BasicClient, AppError> {
|
|||
pub async fn login(
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
Query(mut params): Query<HashMap<String, String>>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Host(hostname): Host,
|
||||
) -> Result<Redirect, AppError> {
|
||||
if user_data.is_some() {
|
||||
|
|
@ -89,21 +88,22 @@ pub async fn login(
|
|||
.set_pkce_challenge(pkce_code_challenge)
|
||||
.url();
|
||||
|
||||
sqlx::query(
|
||||
"INSERT INTO oauth2_state_storage (csrf_state, pkce_code_verifier, return_url) VALUES (?, ?, ?);",
|
||||
sqlx::query!(
|
||||
"INSERT INTO oauth2_state_storage (csrf_state, pkce_code_verifier, return_url) VALUES ($1, $2, $3);",csrf_state.secret(), pkce_code_verifier.secret(), return_url
|
||||
)
|
||||
.bind(csrf_state.secret())
|
||||
.bind(pkce_code_verifier.secret())
|
||||
.bind(return_url)
|
||||
.execute(&db_pool)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|e| {
|
||||
eprintln!("Error inserting into oauth2_state_storage: {}", e);
|
||||
AppError::new("Error inserting into oauth2_state_storage")
|
||||
})?;
|
||||
|
||||
Ok(Redirect::to(authorize_url.as_str()))
|
||||
}
|
||||
|
||||
pub async fn google_auth_return(
|
||||
Query(mut params): Query<HashMap<String, String>>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(db_pool): State<PgPool>,
|
||||
cookie: Option<TypedHeader<Cookie>>,
|
||||
Host(hostname): Host,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
|
|
@ -120,7 +120,7 @@ pub async fn google_auth_return(
|
|||
);
|
||||
|
||||
let query: (String, String) = sqlx::query_as(
|
||||
r#"DELETE FROM oauth2_state_storage WHERE csrf_state = ? RETURNING pkce_code_verifier,return_url"#,
|
||||
r#"DELETE FROM oauth2_state_storage WHERE csrf_state = $1 RETURNING pkce_code_verifier,return_url"#,
|
||||
)
|
||||
.bind(state.secret())
|
||||
.fetch_one(&db_pool)
|
||||
|
|
@ -130,6 +130,7 @@ pub async fn google_auth_return(
|
|||
let _return_url = query.1;
|
||||
let pkce_code_verifier = PkceCodeVerifier::new(pkce_code_verifier);
|
||||
|
||||
|
||||
// Exchange the code with a token.
|
||||
let client = get_client(hostname)?;
|
||||
let token_response = tokio::task::spawn_blocking(move || {
|
||||
|
|
@ -185,21 +186,15 @@ pub async fn google_auth_return(
|
|||
|
||||
// Check if user exists in database
|
||||
// If not, create a new user
|
||||
let query: Result<(i64,), _> = sqlx::query_as(r#"SELECT id FROM users WHERE email=?"#)
|
||||
let query: Result<(uuid::Uuid,), _> = sqlx::query_as(r#"SELECT id FROM users WHERE email=$1"#)
|
||||
.bind(email.as_str())
|
||||
.fetch_one(&db_pool)
|
||||
.await;
|
||||
let user_id = if let Ok(query) = query {
|
||||
query.0
|
||||
} else {
|
||||
let now = Utc::now().timestamp();
|
||||
|
||||
// Add user
|
||||
let query: (i64,) = sqlx::query_as("INSERT INTO users (created_at, created_by, updated_at, updated_by, email, name, family_name, given_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?) RETURNING id")
|
||||
.bind(now)
|
||||
.bind(0 as i64)// Created by system
|
||||
.bind(now)
|
||||
.bind(0 as i64) // Updated by system
|
||||
let query: (uuid::Uuid,) = sqlx::query_as(r#"INSERT INTO users (created_by, updated_by, email, name, family_name, given_name) VALUES ((SELECT id FROM users WHERE "name" = 'admin'), (SELECT id FROM users WHERE "name" = 'admin'), $1, $2, $3, $4) RETURNING id"#)
|
||||
.bind(email.clone())
|
||||
.bind(name.clone())
|
||||
.bind(family_name.clone())
|
||||
|
|
@ -208,13 +203,8 @@ pub async fn google_auth_return(
|
|||
.await?;
|
||||
|
||||
// Add public role
|
||||
sqlx::query("INSERT INTO user_roles (created_at, created_by, updated_at, updated_by, user_id, role_id) VALUES (?, ?, ?, ?, ?, ?)")
|
||||
.bind(now)
|
||||
.bind(0 as i64)// Created by system
|
||||
.bind(now)
|
||||
.bind(0 as i64) // Updated by system
|
||||
sqlx::query(r#"INSERT INTO user_roles (created_by, updated_by, user_id, role_id) VALUES ((SELECT id FROM users WHERE "name" = 'admin'), (SELECT id FROM users WHERE "name" = 'admin'), $1, (SELECT id FROM roles WHERE "name" = 'public'))"#)
|
||||
.bind(query.0)
|
||||
.bind("1")
|
||||
.execute(&db_pool)
|
||||
.await?;
|
||||
|
||||
|
|
@ -242,18 +232,15 @@ pub async fn google_auth_return(
|
|||
session_token
|
||||
)
|
||||
).map_err(|_| AppError::new("Failed to create session token header"))?;
|
||||
let now = Utc::now().timestamp();
|
||||
|
||||
sqlx::query(
|
||||
"INSERT INTO user_sessions
|
||||
(session_token_p1, session_token_p2, user_id, created_at, expires_at)
|
||||
VALUES (?, ?, ?, ?, ?);",
|
||||
VALUES ($1, $2, $3, now(), now() + interval '1 day');",
|
||||
)
|
||||
.bind(session_token_p1)
|
||||
.bind(session_token_p2)
|
||||
.bind(user_id) // Set user to anonymous
|
||||
.bind(now)
|
||||
.bind(now + 60 * 60 * 24)
|
||||
.execute(&db_pool)
|
||||
.await?;
|
||||
}
|
||||
|
|
@ -263,12 +250,12 @@ pub async fn google_auth_return(
|
|||
|
||||
pub async fn logout(
|
||||
cookie: Option<TypedHeader<Cookie>>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(db_pool): State<PgPool>,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
if let Some(cookie) = cookie {
|
||||
if let Some(session_token) = cookie.get("session_token") {
|
||||
let session_token: Vec<&str> = session_token.split('_').collect();
|
||||
let _ = sqlx::query("DELETE FROM user_sessions WHERE session_token_1 = ?")
|
||||
let _ = sqlx::query("DELETE FROM user_sessions WHERE session_token_1 = $1")
|
||||
.bind(session_token[0])
|
||||
.execute(&db_pool)
|
||||
.await;
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ use axum::{
|
|||
routing::{get, get_service},
|
||||
Extension, Router,
|
||||
};
|
||||
use dotenvy::var;
|
||||
use secret_gift_exchange::{giftexchange, giftexchange_save, giftexchanges};
|
||||
use sqlx::migrate::Migrator;
|
||||
use sqlx::{sqlite::SqlitePoolOptions, SqlitePool};
|
||||
use sqlx::{migrate::Migrator, sqlite::SqlitePoolOptions, sqlite::SqliteRow, Row, SqlitePool};
|
||||
use sqlx::{postgres::PgPoolOptions, PgPool};
|
||||
use std::net::SocketAddr;
|
||||
use tower_http::services::ServeDir;
|
||||
|
||||
|
|
@ -15,22 +16,26 @@ mod error_handling;
|
|||
mod google_oauth;
|
||||
mod middlewares;
|
||||
mod routes;
|
||||
mod secret_gift_exchange;
|
||||
mod user;
|
||||
mod wishlist;
|
||||
mod secret_gift_exchange;
|
||||
|
||||
use calendar::{calendar, get_events};
|
||||
use error_handling::AppError;
|
||||
use google_oauth::{google_auth_return, login, logout};
|
||||
use middlewares::inject_user_data;
|
||||
use routes::{about, contact, dashboard, index, profile, user_profile, useradmin};
|
||||
use user::{add_user_role, delete_user_role, UserData};
|
||||
use calendar::{calendar, get_events};
|
||||
use wishlist::{user_wishlist, user_wishlist_add, user_wishlist_add_item, user_wishlist_bought_item, user_wishlist_delete_item, user_wishlist_edit_item, user_wishlist_received_item, user_wishlist_returned_item, user_wishlist_save_item, wishlists};
|
||||
use wishlist::{
|
||||
user_wishlist, user_wishlist_add, user_wishlist_add_item, user_wishlist_bought_item,
|
||||
user_wishlist_delete_item, user_wishlist_edit_item, user_wishlist_received_item,
|
||||
user_wishlist_returned_item, user_wishlist_save_item, wishlists,
|
||||
};
|
||||
//use email::send_emails;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppState {
|
||||
pub db_pool: SqlitePool,
|
||||
pub db_pool: PgPool,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
|
|
@ -38,14 +43,11 @@ async fn main() {
|
|||
// initialize tracing
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let db_pool = SqlitePoolOptions::new()
|
||||
.max_connections(5)
|
||||
.connect("sqlite://db/db.sqlite3")
|
||||
.await;
|
||||
// Get the server settings from the env file
|
||||
let database_url = var("DATABASE_URL").expect("DATABASE_URL not set");
|
||||
let db_pool = PgPoolOptions::new().connect(&database_url).await.unwrap();
|
||||
|
||||
let app_state = AppState {
|
||||
db_pool: db_pool.expect("Failed to get db_pool"),
|
||||
};
|
||||
let app_state = AppState { db_pool: db_pool };
|
||||
|
||||
static MIGRATOR: Migrator = sqlx::migrate!();
|
||||
|
||||
|
|
@ -54,39 +56,74 @@ async fn main() {
|
|||
.await
|
||||
.expect("Failed to run migrations");
|
||||
|
||||
// Copy from old sqlite database if it exists
|
||||
if let Ok(source_db_url) = var("SOURCE_DB_URL") {
|
||||
let sdb_pool = SqlitePoolOptions::new()
|
||||
.max_connections(5)
|
||||
.connect(&source_db_url)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
copy_database(&sdb_pool, &app_state.db_pool).await;
|
||||
} else {
|
||||
println!("SOURCE_DB_URL not set");
|
||||
}
|
||||
|
||||
let user_data: Option<UserData> = None;
|
||||
|
||||
// build our application with some routes
|
||||
let app = Router::new()
|
||||
.route("/dashboard", get(dashboard))
|
||||
|
||||
// User
|
||||
.route("/profile", get(profile))
|
||||
.route("/useradmin", get(useradmin))
|
||||
.route("/users/:user_id", get(user_profile))
|
||||
.route("/roles/:user_id/:role_id/add", get(add_user_role))
|
||||
.route("/roles/:user_id/:user_role_id/delete", get(delete_user_role))
|
||||
|
||||
.route(
|
||||
"/roles/:user_id/:user_role_id/delete",
|
||||
get(delete_user_role),
|
||||
)
|
||||
// Calendar
|
||||
.route("/calendar", get(calendar))
|
||||
.route("/getevents/:calendar", get(get_events))
|
||||
|
||||
// Wishlist
|
||||
.route("/wishlists", get(wishlists))
|
||||
.route("/userwishlist/:user_id", get(user_wishlist))
|
||||
.route("/userwishlist/add/:user_id", get(user_wishlist_add).post(user_wishlist_add_item))
|
||||
.route("/userwishlist/edit/:item_id", get(user_wishlist_edit_item).post(user_wishlist_save_item))
|
||||
.route("/userwishlist/bought/:user_id", get(user_wishlist_bought_item))
|
||||
.route("/userwishlist/received/:user_id", get(user_wishlist_received_item))
|
||||
.route("/userwishlist/delete/:item_id", get(user_wishlist_delete_item))
|
||||
.route("/userwishlist/returned/:item_id", get(user_wishlist_returned_item))
|
||||
|
||||
.route(
|
||||
"/userwishlist/add/:user_id",
|
||||
get(user_wishlist_add).post(user_wishlist_add_item),
|
||||
)
|
||||
.route(
|
||||
"/userwishlist/edit/:item_id",
|
||||
get(user_wishlist_edit_item).post(user_wishlist_save_item),
|
||||
)
|
||||
.route(
|
||||
"/userwishlist/bought/:user_id",
|
||||
get(user_wishlist_bought_item),
|
||||
)
|
||||
.route(
|
||||
"/userwishlist/received/:user_id",
|
||||
get(user_wishlist_received_item),
|
||||
)
|
||||
.route(
|
||||
"/userwishlist/delete/:item_id",
|
||||
get(user_wishlist_delete_item),
|
||||
)
|
||||
.route(
|
||||
"/userwishlist/returned/:item_id",
|
||||
get(user_wishlist_returned_item),
|
||||
)
|
||||
// Secret Gift Exchange - Not ready for public use yet
|
||||
.route("/giftexchanges", get(giftexchanges))
|
||||
.route("/giftexchange/:giftexchange_id", get(giftexchange).post(giftexchange_save))
|
||||
|
||||
.nest_service("/assets", ServeDir::new("templates/assets")
|
||||
.fallback(get_service(ServeDir::new("templates/assets"))))
|
||||
.route(
|
||||
"/giftexchange/:giftexchange_id",
|
||||
get(giftexchange).post(giftexchange_save),
|
||||
)
|
||||
.nest_service(
|
||||
"/assets",
|
||||
ServeDir::new("templates/assets")
|
||||
.fallback(get_service(ServeDir::new("templates/assets"))),
|
||||
)
|
||||
.route("/", get(index))
|
||||
.route("/about", get(about))
|
||||
.route("/contactus", get(contact))
|
||||
|
|
@ -112,3 +149,169 @@ async fn main() {
|
|||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
async fn copy_database(sdb_pool: &SqlitePool, db_pool: &PgPool) {
|
||||
// Copy users
|
||||
let users = sqlx::query(
|
||||
r#"select
|
||||
datetime(u.created_at, 'unixepoch'),
|
||||
coalesce(cb.email, 'admin@jean-marie.ca') as created_by_email,
|
||||
datetime(u.updated_at, 'unixepoch'),
|
||||
coalesce(ub.email, 'admin@jean-marie.ca') as updated_by_email,
|
||||
u.email,
|
||||
u.name,
|
||||
u.family_name,
|
||||
u.given_name
|
||||
from users u
|
||||
left join users cb on cb.id = u.created_by
|
||||
left join users ub on ub.id = u.updated_by;"#,
|
||||
)
|
||||
.fetch_all(sdb_pool)
|
||||
.await
|
||||
.expect("Failed to copy users from SQLite to Postgres");
|
||||
|
||||
println!("\nCopying {} users", users.len());
|
||||
|
||||
for user in users {
|
||||
if let (
|
||||
Ok(created_at),
|
||||
Ok(created_by),
|
||||
Ok(updated_at),
|
||||
Ok(updated_by),
|
||||
Ok(email),
|
||||
Ok(name),
|
||||
Ok(family_name),
|
||||
Ok(given_name),
|
||||
) = (
|
||||
user.try_get::<chrono::NaiveDateTime, _>(0),
|
||||
user.try_get::<String, _>(1),
|
||||
user.try_get::<chrono::NaiveDateTime, _>(2),
|
||||
user.try_get::<String, _>(3),
|
||||
user.try_get::<String, _>(4),
|
||||
user.try_get::<String, _>(5),
|
||||
user.try_get::<String, _>(6),
|
||||
user.try_get::<String, _>(7),
|
||||
) {
|
||||
let result = sqlx::query(
|
||||
r#"insert into users (created_at, created_by, updated_at, updated_by, email, name, family_name, given_name)
|
||||
values ($1, (select id from users where email =$2), $3, (select id from users where email =$4), $5, $6, $7, $8)"#
|
||||
)
|
||||
.bind(created_at)
|
||||
.bind(created_by)
|
||||
.bind(updated_at)
|
||||
.bind(updated_by)
|
||||
.bind(email)
|
||||
.bind(name)
|
||||
.bind(family_name)
|
||||
.bind(given_name)
|
||||
.execute(db_pool)
|
||||
.await;
|
||||
|
||||
if let Err(e) = result {
|
||||
println!("Error: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy user roles
|
||||
let user_roles = sqlx::query(
|
||||
r#"select
|
||||
datetime(ur.created_at, 'unixepoch'),
|
||||
coalesce(cb.email, 'admin@jean-marie.ca') as created_by_email,
|
||||
datetime(ur.updated_at, 'unixepoch'),
|
||||
coalesce(ub.email, 'admin@jean-marie.ca') as updated_by_email,
|
||||
u.email as user_email,
|
||||
r.name as role_name
|
||||
from user_roles ur
|
||||
left join users cb on cb.id = ur.created_by
|
||||
left join users ub on ub.id = ur.updated_by
|
||||
join users u on u.id = ur.user_id
|
||||
join roles r on r.id = ur.role_id;"#,
|
||||
)
|
||||
.fetch_all(sdb_pool)
|
||||
.await
|
||||
.expect("Failed to copy user roles from SQLite to Postgres");
|
||||
|
||||
println!("\nCopying {} user roles", user_roles.len());
|
||||
|
||||
for user_role in user_roles {
|
||||
if let (
|
||||
Ok(created_at),
|
||||
Ok(created_by),
|
||||
Ok(updated_at),
|
||||
Ok(updated_by),
|
||||
Ok(user_email),
|
||||
Ok(role_name),
|
||||
) = (
|
||||
user_role.try_get::<chrono::NaiveDateTime, _>(0),
|
||||
user_role.try_get::<String, _>(1),
|
||||
user_role.try_get::<chrono::NaiveDateTime, _>(2),
|
||||
user_role.try_get::<String, _>(3),
|
||||
user_role.try_get::<String, _>(4),
|
||||
user_role.try_get::<String, _>(5),
|
||||
) {
|
||||
let result = sqlx::query(
|
||||
r#"insert into user_roles (created_at, created_by, updated_at, updated_by, user_id, role_id)
|
||||
values ($1, (select id from users where email=$2), $3, (select id from users where email=$4), (select id from users where email=$5), (select id from roles where name=$6))"#
|
||||
)
|
||||
.bind(created_at)
|
||||
.bind(created_by)
|
||||
.bind(updated_at)
|
||||
.bind(updated_by)
|
||||
.bind(user_email)
|
||||
.bind(role_name)
|
||||
.execute(db_pool)
|
||||
.await;
|
||||
|
||||
if let Err(e) = result {
|
||||
println!("Error: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy wishlistitems
|
||||
let wishlistitems = sqlx::query(
|
||||
r#"select
|
||||
datetime(wi.created_at, 'unixepoch'),
|
||||
coalesce(cb.email, 'admin@jean-marie.ca') as created_by_email,
|
||||
datetime(wi.updated_at, 'unixepoch'),
|
||||
coalesce(ub.email, 'admin@jean-marie.ca') as updated_by_email,
|
||||
u.email as user_email,
|
||||
wi.item,
|
||||
wi.item_url,
|
||||
pb.email,
|
||||
datetime(wi.received_at, 'unixepoch')
|
||||
from wishlist_items wi
|
||||
left join users cb on cb.id = wi.created_by
|
||||
left join users ub on ub.id = wi.updated_by
|
||||
left join users pb on pb.id = wi.purchased_by
|
||||
join users u on u.id = wi.user_id;"#,
|
||||
)
|
||||
.fetch_all(sdb_pool)
|
||||
.await
|
||||
.expect("Failed to copy wishlistitems from SQLite to Postgres");
|
||||
|
||||
println!("\nCopying {} wishlistitems", wishlistitems.len());
|
||||
|
||||
for wishlistitem in wishlistitems {
|
||||
let result = sqlx::query(
|
||||
r#"insert into wishlist_items (created_at, created_by, updated_at, updated_by, user_id, item, item_url, purchased_by, received_at)
|
||||
values ($1, (select id from users where email=$2), $3, (select id from users where email=$4), (select id from users where email=$5), $6, $7, (select id from users where email=$8), $9)"#
|
||||
)
|
||||
.bind(wishlistitem.try_get::<chrono::NaiveDateTime,_>(0).unwrap())
|
||||
.bind(wishlistitem.try_get::<String,_>(1).unwrap())
|
||||
.bind(wishlistitem.try_get::<chrono::NaiveDateTime,_>(2).unwrap())
|
||||
.bind(wishlistitem.try_get::<String,_>(3).unwrap())
|
||||
.bind(wishlistitem.try_get::<String,_>(4).unwrap())
|
||||
.bind(wishlistitem.try_get::<String,_>(5).unwrap())
|
||||
.bind(wishlistitem.try_get::<String,_>(6).unwrap())
|
||||
.bind(wishlistitem.try_get::<String,_>(7).unwrap())
|
||||
.bind(wishlistitem.try_get::<chrono::NaiveDateTime,_>(8).unwrap_or_default())
|
||||
.execute(db_pool)
|
||||
.await;
|
||||
|
||||
if let Err(e) = result {
|
||||
println!("Error: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,20 +1,14 @@
|
|||
use std::path::Path;
|
||||
|
||||
use super::{AppError, UserData};
|
||||
use axum::{
|
||||
body::Body,
|
||||
extract::State,
|
||||
http::Request,
|
||||
middleware::Next,
|
||||
response::IntoResponse,
|
||||
};
|
||||
use axum::{body::Body, extract::State, http::Request, middleware::Next, response::IntoResponse};
|
||||
use axum_extra::TypedHeader;
|
||||
use chrono::Utc;
|
||||
use headers::Cookie;
|
||||
use sqlx::SqlitePool;
|
||||
use sqlx::PgPool;
|
||||
|
||||
pub async fn inject_user_data(
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(db_pool): State<PgPool>,
|
||||
cookie: Option<TypedHeader<Cookie>>,
|
||||
mut request: Request<Body>,
|
||||
next: Next,
|
||||
|
|
@ -22,8 +16,8 @@ pub async fn inject_user_data(
|
|||
if let Some(cookie) = cookie {
|
||||
if let Some(session_token) = cookie.get("session_token") {
|
||||
let session_token: Vec<&str> = session_token.split('_').collect();
|
||||
let query: Result<(i64, i64, String), _> = sqlx::query_as(
|
||||
r#"SELECT user_id,expires_at,session_token_p2 FROM user_sessions WHERE session_token_p1=?"#,
|
||||
let query: Result<(uuid::Uuid, chrono::NaiveDateTime, String), _> = sqlx::query_as(
|
||||
r#"SELECT user_id,expires_at,session_token_p2 FROM user_sessions WHERE session_token_p1=$1"#,
|
||||
)
|
||||
.bind(session_token[0])
|
||||
.fetch_one(&db_pool)
|
||||
|
|
@ -44,14 +38,14 @@ pub async fn inject_user_data(
|
|||
) {
|
||||
let id = query.0;
|
||||
let expires_at = query.1;
|
||||
if expires_at > Utc::now().timestamp() {
|
||||
let row = sqlx::query_as!(
|
||||
UserData,
|
||||
"SELECT * FROM users WHERE id = ?",
|
||||
id
|
||||
)
|
||||
if expires_at > Utc::now().naive_local() {
|
||||
let row: UserData = sqlx::query_as(
|
||||
r#"SELECT id, created_at, created_by, updated_at, updated_by, email, name, family_name, given_name FROM users WHERE id = $1"#,
|
||||
)
|
||||
.bind(id)
|
||||
.fetch_one(&db_pool)
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
request.extensions_mut().insert(Some(UserData {
|
||||
id: row.id,
|
||||
|
|
@ -62,7 +56,7 @@ pub async fn inject_user_data(
|
|||
email: row.email,
|
||||
name: row.name,
|
||||
family_name: row.family_name,
|
||||
given_name: row.given_name
|
||||
given_name: row.given_name,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
@ -75,9 +69,9 @@ pub async fn inject_user_data(
|
|||
Ok(next.run(request).await)
|
||||
}
|
||||
|
||||
pub async fn is_authorized(path: &str, user_data: Option<UserData>, db_pool: SqlitePool) -> bool {
|
||||
pub async fn is_authorized(path: &str, user_data: Option<UserData>, db_pool: PgPool) -> bool {
|
||||
if let Some(user_data) = user_data {
|
||||
let query: Result<(i64,), _> = match path {
|
||||
let query: Result<(uuid::Uuid,), _> = match path {
|
||||
"/profile" => {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -85,10 +79,11 @@ pub async fn is_authorized(path: &str, user_data: Option<UserData>, db_pool: Sql
|
|||
// loop through path to find a permission
|
||||
let mut remaining_path = Path::new(path);
|
||||
loop {
|
||||
let query: Result<(String,), _> = sqlx::query_as(r#"select r.item from role_permissions r where item = ?"#)
|
||||
.bind(remaining_path.to_str().unwrap())
|
||||
.fetch_one(&db_pool)
|
||||
.await;
|
||||
let query: Result<(String,), _> =
|
||||
sqlx::query_as(r#"select r.item from role_permissions r where item = $1"#)
|
||||
.bind(remaining_path.to_str().unwrap())
|
||||
.fetch_one(&db_pool)
|
||||
.await;
|
||||
if let Ok(query) = query {
|
||||
if query.0 != "" {
|
||||
break;
|
||||
|
|
@ -99,7 +94,7 @@ pub async fn is_authorized(path: &str, user_data: Option<UserData>, db_pool: Sql
|
|||
}
|
||||
remaining_path = remaining_path.parent().unwrap();
|
||||
}
|
||||
sqlx::query_as(r#"select u.id from role_permissions r join user_roles ur on ur.role_id = r.role_id join users u on u.id = ur.user_id where item = ? and email = ?"#)
|
||||
sqlx::query_as(r#"select u.id from role_permissions r join user_roles ur on ur.role_id = r.role_id join users u on u.id = ur.user_id where item = $1 and email = $2"#)
|
||||
.bind(remaining_path.to_str().unwrap())
|
||||
.bind(user_data.email.as_str())
|
||||
.fetch_one(&db_pool)
|
||||
|
|
@ -114,4 +109,4 @@ pub async fn is_authorized(path: &str, user_data: Option<UserData>, db_pool: Sql
|
|||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use axum::{
|
|||
Extension,
|
||||
};
|
||||
use http::StatusCode;
|
||||
use sqlx::SqlitePool;
|
||||
use sqlx::PgPool;
|
||||
|
||||
use crate::{
|
||||
middlewares::is_authorized,
|
||||
|
|
@ -67,7 +67,7 @@ struct DashboardTemplate {
|
|||
}
|
||||
|
||||
pub async fn index(
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
|
|
@ -93,7 +93,7 @@ pub async fn index(
|
|||
}
|
||||
|
||||
pub async fn dashboard(
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
|
|
@ -127,7 +127,7 @@ pub async fn dashboard(
|
|||
|
||||
/// Handles the profile page.
|
||||
pub async fn profile(
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
|
|
@ -158,8 +158,8 @@ pub async fn profile(
|
|||
}
|
||||
|
||||
pub async fn user_profile(
|
||||
Path(user_id): Path<i64>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
Path(user_id): Path<uuid::Uuid>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
|
|
@ -171,7 +171,8 @@ pub async fn user_profile(
|
|||
let userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
|
||||
|
||||
// Extract the user data.
|
||||
let profile = sqlx::query_as!(UserData, "SELECT * FROM users WHERE id = ?", user_id)
|
||||
let profile = sqlx::query_as( "SELECT * FROM users WHERE id = $1")
|
||||
.bind(user_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
@ -215,7 +216,7 @@ struct UserAdminTemplate {
|
|||
|
||||
pub async fn useradmin(
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(db_pool): State<PgPool>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
let logged_in = user_data.is_some();
|
||||
|
|
@ -261,17 +262,7 @@ pub async fn about(Extension(user_data): Extension<Option<UserData>>) -> impl In
|
|||
let logged_in = user_data.is_some();
|
||||
|
||||
// Set empty user
|
||||
let mut user = UserData {
|
||||
id: 0,
|
||||
email: "".to_string(),
|
||||
created_at: 0,
|
||||
created_by: 0,
|
||||
updated_at: 0,
|
||||
updated_by: 0,
|
||||
name: "".to_string(),
|
||||
family_name: "".to_string(),
|
||||
given_name: "".to_string(),
|
||||
};
|
||||
let mut user = UserData::default();
|
||||
|
||||
if logged_in {
|
||||
// Extract the user data.
|
||||
|
|
@ -294,17 +285,7 @@ pub async fn contact(Extension(user_data): Extension<Option<UserData>>) -> impl
|
|||
let logged_in = user_data.is_some();
|
||||
|
||||
// Set empty user
|
||||
let mut user = UserData {
|
||||
id: 0,
|
||||
email: "".to_string(),
|
||||
created_at: 0,
|
||||
created_by: 0,
|
||||
updated_at: 0,
|
||||
updated_by: 0,
|
||||
name: "".to_string(),
|
||||
family_name: "".to_string(),
|
||||
given_name: "".to_string(),
|
||||
};
|
||||
let mut user = UserData::default();
|
||||
|
||||
if logged_in {
|
||||
// Extract the user data.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use askama::Template;
|
||||
use askama_axum::{IntoResponse, Response};
|
||||
use axum::{
|
||||
|
|
@ -9,10 +7,9 @@ use axum::{
|
|||
Extension, Form, Json, RequestExt,
|
||||
};
|
||||
use axum_extra::response::Html;
|
||||
use chrono::Utc;
|
||||
use http::{header::CONTENT_TYPE, StatusCode};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, SqlitePool};
|
||||
use sqlx::{FromRow, PgPool};
|
||||
|
||||
use crate::{
|
||||
middlewares::is_authorized,
|
||||
|
|
@ -99,7 +96,7 @@ struct GiftExchangesTemplate {
|
|||
|
||||
pub async fn giftexchanges(
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(db_pool): State<PgPool>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
let logged_in = user_data.is_some();
|
||||
|
|
@ -159,7 +156,7 @@ struct GiftExchangeTemplate {
|
|||
pub async fn giftexchange(
|
||||
Path(exchange_id): Path<i64>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(db_pool): State<PgPool>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
let logged_in = user_data.is_some();
|
||||
|
|
@ -174,11 +171,9 @@ pub async fn giftexchange(
|
|||
let user_roles = get_user_roles_display(userid, &db_pool.clone()).await;
|
||||
|
||||
// Get gift exchange
|
||||
let giftexchange = match sqlx::query_as!(
|
||||
GiftExchange,
|
||||
"SELECT * FROM gift_exchange WHERE id = ?",
|
||||
exchange_id
|
||||
)
|
||||
let giftexchange = match sqlx::query_as(
|
||||
"SELECT * FROM gift_exchange WHERE id = ?")
|
||||
.bind(exchange_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
{
|
||||
|
|
@ -188,7 +183,7 @@ pub async fn giftexchange(
|
|||
|
||||
// Get participants
|
||||
let participants = sqlx::query_as::<_, UserData>(
|
||||
"select * from users where users.id in (select participant_id from gift_exchange_participants where exchange_id = ?)",
|
||||
"select * from users where users.id in (select participant_id from gift_exchange_participants where exchange_id = $1)",
|
||||
)
|
||||
.bind(exchange_id)
|
||||
.fetch_all(&db_pool)
|
||||
|
|
@ -197,7 +192,7 @@ pub async fn giftexchange(
|
|||
|
||||
// Get non participants
|
||||
let non_participants = sqlx::query_as::<_, UserData>(
|
||||
"select * from users where users.id not in (select participant_id from gift_exchange_participants where exchange_id = ?)",
|
||||
"select * from users where users.id not in (select participant_id from gift_exchange_participants where exchange_id = $1)",
|
||||
)
|
||||
.bind(exchange_id)
|
||||
.fetch_all(&db_pool)
|
||||
|
|
@ -225,15 +220,15 @@ pub async fn giftexchange(
|
|||
pub struct ExchangeForm {
|
||||
name: String,
|
||||
exchange_date: String,
|
||||
non_participants: Vec<i64>,
|
||||
non_participants: Vec<uuid::Uuid>,
|
||||
}
|
||||
|
||||
pub async fn giftexchange_save(
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(_db_pool): State<PgPool>,
|
||||
request: Request<Body>,
|
||||
) -> impl IntoResponse {
|
||||
let content_type_header = request.headers().get(CONTENT_TYPE);
|
||||
let content_type = content_type_header.and_then(|value| value.to_str().ok());
|
||||
let _content_type = content_type_header.and_then(|value| value.to_str().ok());
|
||||
|
||||
/* if let Some(content_type) = content_type {
|
||||
if content_type.starts_with("application/json") {
|
||||
|
|
|
|||
|
|
@ -4,21 +4,20 @@ use axum::{
|
|||
response::Redirect,
|
||||
Extension,
|
||||
};
|
||||
use chrono::Utc;
|
||||
|
||||
///User related structs and functions
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::{FromRow, SqlitePool};
|
||||
use sqlx::{FromRow, PgPool};
|
||||
|
||||
use crate::middlewares::is_authorized;
|
||||
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, FromRow)]
|
||||
pub struct UserData {
|
||||
pub id: i64,
|
||||
pub created_at: i64,
|
||||
pub created_by: i64,
|
||||
pub updated_at: i64,
|
||||
pub updated_by: i64,
|
||||
pub id: uuid::Uuid,
|
||||
pub created_at: chrono::NaiveDateTime,
|
||||
pub created_by: uuid::Uuid,
|
||||
pub updated_at: chrono::NaiveDateTime,
|
||||
pub updated_by: uuid::Uuid,
|
||||
pub email: String,
|
||||
pub name: String,
|
||||
pub family_name: String,
|
||||
|
|
@ -27,58 +26,58 @@ pub struct UserData {
|
|||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct RoleData {
|
||||
pub id: i64,
|
||||
pub created_at: i64,
|
||||
pub created_by: i64,
|
||||
pub updated_at: i64,
|
||||
pub updated_by: i64,
|
||||
pub id: uuid::Uuid,
|
||||
pub created_at: chrono::NaiveDateTime,
|
||||
pub created_by: uuid::Uuid,
|
||||
pub updated_at: chrono::NaiveDateTime,
|
||||
pub updated_by: uuid::Uuid,
|
||||
pub name: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct UserRoles {
|
||||
pub id: i64,
|
||||
pub created_at: i64,
|
||||
pub created_by: i64,
|
||||
pub updated_at: i64,
|
||||
pub updated_by: i64,
|
||||
pub user_id: i64,
|
||||
pub role_id: i64,
|
||||
pub id: uuid::Uuid,
|
||||
pub created_at: chrono::NaiveDateTime,
|
||||
pub created_by: uuid::Uuid,
|
||||
pub updated_at: chrono::NaiveDateTime,
|
||||
pub updated_by: uuid::Uuid,
|
||||
pub user_id: uuid::Uuid,
|
||||
pub role_id: uuid::Uuid,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, FromRow)]
|
||||
pub struct UserRolesDisplay {
|
||||
pub id: i64,
|
||||
pub created_at: i64,
|
||||
pub created_by: i64,
|
||||
pub updated_at: i64,
|
||||
pub updated_by: i64,
|
||||
pub user_id: i64,
|
||||
pub id: uuid::Uuid,
|
||||
pub created_at: chrono::NaiveDateTime,
|
||||
pub created_by: uuid::Uuid,
|
||||
pub updated_at: chrono::NaiveDateTime,
|
||||
pub updated_by: uuid::Uuid,
|
||||
pub user_id: uuid::Uuid,
|
||||
pub user_name: String,
|
||||
pub role_id: i64,
|
||||
pub role_id: uuid::Uuid,
|
||||
pub role_name: String,
|
||||
}
|
||||
|
||||
#[derive(Default, Clone, Debug, Serialize, Deserialize, FromRow)]
|
||||
pub struct UserWishlistItem {
|
||||
pub id: i64,
|
||||
pub created_at: i64,
|
||||
pub created_by: i64,
|
||||
pub updated_at: i64,
|
||||
pub updated_by: i64,
|
||||
pub user_id: i64,
|
||||
pub id: uuid::Uuid,
|
||||
pub created_at: chrono::NaiveDateTime,
|
||||
pub created_by: uuid::Uuid,
|
||||
pub updated_at: chrono::NaiveDateTime,
|
||||
pub updated_by: uuid::Uuid,
|
||||
pub user_id: uuid::Uuid,
|
||||
pub item: String,
|
||||
pub item_url: String,
|
||||
pub purchased_by: i64,
|
||||
pub received_at: i64,
|
||||
pub purchased_by: Option<uuid::Uuid>,
|
||||
pub received_at: Option<chrono::NaiveDateTime>,
|
||||
}
|
||||
|
||||
/*
|
||||
pub async fn get_user_roles(user_id: i64, db_pool: &SqlitePool) -> Vec<UserRoles> {
|
||||
pub async fn get_user_roles(user_id: i64, db_pool: &PgPool) -> Vec<UserRoles> {
|
||||
// Get user roles
|
||||
let user_roles = sqlx::query_as(
|
||||
r#"SELECT id, created_at, created_by, updated_at, updated_by, user_id, role_id FROM user_roles WHERE user_id = ?"#
|
||||
r#"SELECT id, created_at, created_by, updated_at, updated_by, user_id, role_id FROM user_roles WHERE user_id = $1"#
|
||||
)
|
||||
.bind(user_id)
|
||||
.fetch_all(db_pool)
|
||||
|
|
@ -88,10 +87,10 @@ pub async fn get_user_roles(user_id: i64, db_pool: &SqlitePool) -> Vec<UserRoles
|
|||
user_roles
|
||||
} */
|
||||
|
||||
pub async fn get_user_roles_display(user_id: i64, db_pool: &SqlitePool) -> Vec<UserRolesDisplay> {
|
||||
pub async fn get_user_roles_display(user_id: uuid::Uuid, db_pool: &PgPool) -> Vec<UserRolesDisplay> {
|
||||
// Get user roles
|
||||
let user_roles = sqlx::query_as(
|
||||
r#"select ur.id, u.id as user_id, u.name as user_name, r.id as role_id, r.name as role_name, r.created_at, r.created_by, r.updated_at, r.updated_by from roles r join user_roles ur on ur.role_id = r.id join users u on u.id = ur.user_id WHERE ur.user_id = ?"#
|
||||
r#"select ur.id, u.id as user_id, u.name as user_name, r.id as role_id, r.name as role_name, r.created_at, r.created_by, r.updated_at, r.updated_by from roles r join user_roles ur on ur.role_id = r.id join users u on u.id = ur.user_id WHERE ur.user_id = $1"#
|
||||
)
|
||||
.bind(user_id)
|
||||
.fetch_all(db_pool)
|
||||
|
|
@ -101,10 +100,10 @@ pub async fn get_user_roles_display(user_id: i64, db_pool: &SqlitePool) -> Vec<U
|
|||
user_roles
|
||||
}
|
||||
|
||||
pub async fn get_other_roles_display(user_id: i64, db_pool: &SqlitePool) -> Vec<UserRolesDisplay> {
|
||||
pub async fn get_other_roles_display(user_id: uuid::Uuid, db_pool: &PgPool) -> Vec<UserRolesDisplay> {
|
||||
// Get roles user does not have
|
||||
let user_roles = sqlx::query_as(
|
||||
r#"select 0 as id, r.created_at, r.created_by, r.updated_at, r.updated_by, ? as user_id, '' as user_name, r.id as role_id, r.name as role_name from roles r where r.id not in (select ur.role_id from user_roles ur where ur.user_id = ?)"#
|
||||
r#"select r.id as id, r.created_at, r.created_by, r.updated_at, r.updated_by, $1 as user_id, '' as user_name, r.id as role_id, r.name as role_name from roles r where r.id not in (select ur.role_id from user_roles ur where ur.user_id = $2)"#
|
||||
)
|
||||
.bind(user_id.clone())
|
||||
.bind(user_id)
|
||||
|
|
@ -116,17 +115,13 @@ pub async fn get_other_roles_display(user_id: i64, db_pool: &SqlitePool) -> Vec<
|
|||
}
|
||||
|
||||
pub async fn add_user_role(
|
||||
Path((user_id, role_id)): Path<(i64, i64)>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
Path((user_id, role_id)): Path<(uuid::Uuid, uuid::Uuid)>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
if is_authorized("/roles", user_data.clone(), db_pool.clone()).await {
|
||||
let now = Utc::now().timestamp();
|
||||
|
||||
sqlx::query("INSERT INTO user_roles (created_at, created_by, updated_at, updated_by, user_id, role_id) VALUES (?, ?, ?, ?, ?, ?)")
|
||||
.bind(now)// Created now
|
||||
sqlx::query("INSERT INTO user_roles (created_by, updated_by, user_id, role_id) VALUES ($1, $2, $3, $4)")
|
||||
.bind(user_data.as_ref().unwrap().id)// Created by current user
|
||||
.bind(now) // Updated now
|
||||
.bind(user_data.as_ref().unwrap().id) // Updated by current user
|
||||
.bind(user_id)
|
||||
.bind(role_id)
|
||||
|
|
@ -144,12 +139,12 @@ pub async fn add_user_role(
|
|||
}
|
||||
|
||||
pub async fn delete_user_role(
|
||||
Path((user_id, user_role_id)): Path<(i64, i64)>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
Path((user_id, user_role_id)): Path<(uuid::Uuid, uuid::Uuid)>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
if is_authorized("/roles", user_data, db_pool.clone()).await {
|
||||
sqlx::query("DELETE FROM user_roles WHERE id = ?")
|
||||
sqlx::query("DELETE FROM user_roles WHERE id = $1")
|
||||
.bind(user_role_id)
|
||||
.execute(&db_pool)
|
||||
.await
|
||||
|
|
@ -162,11 +157,11 @@ pub async fn delete_user_role(
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn get_user_wishlist_item_by_id(item_id: i64, db_pool: &SqlitePool) -> UserWishlistItem {
|
||||
pub async fn get_user_wishlist_item_by_id(item_id: uuid::Uuid, db_pool: &PgPool) -> UserWishlistItem {
|
||||
// Get wish list items for the user
|
||||
let user_wishlist_item = sqlx::query_as(
|
||||
r#"select id, created_at, created_by, updated_at, updated_by, user_id, item, item_url, purchased_by, received_at
|
||||
from wishlist_items where id = ?"#
|
||||
from wishlist_items where id = $1"#
|
||||
)
|
||||
.bind(item_id)
|
||||
.fetch_one(db_pool)
|
||||
|
|
@ -176,11 +171,11 @@ pub async fn get_user_wishlist_item_by_id(item_id: i64, db_pool: &SqlitePool) ->
|
|||
user_wishlist_item
|
||||
}
|
||||
|
||||
pub async fn get_user_wishlist_items(user_id: i64, db_pool: &SqlitePool) -> Vec<UserWishlistItem> {
|
||||
pub async fn get_user_wishlist_items(user_id: uuid::Uuid, db_pool: &PgPool) -> Vec<UserWishlistItem> {
|
||||
// Get wish list items for the user
|
||||
let user_wishlist_items = sqlx::query_as(
|
||||
r#"select id, created_at, created_by, updated_at, updated_by, user_id, item, item_url, purchased_by, received_at
|
||||
from wishlist_items where user_id = ?"#
|
||||
from wishlist_items where user_id = $1"#
|
||||
)
|
||||
.bind(user_id)
|
||||
.fetch_all(db_pool)
|
||||
|
|
@ -190,8 +185,8 @@ pub async fn get_user_wishlist_items(user_id: i64, db_pool: &SqlitePool) -> Vec<
|
|||
user_wishlist_items
|
||||
}
|
||||
|
||||
pub async fn get_useremails_by_role(role_name: String, db_pool: &SqlitePool) -> String {
|
||||
let useremails: String = sqlx::query_scalar(r#"select group_concat(u.email) as email from user_roles ur, roles r, users u where u.id = ur.user_id and r.id = ur.role_id and r.name = ?"#)
|
||||
pub async fn get_useremails_by_role(role_name: String, db_pool: &PgPool) -> String {
|
||||
let useremails: String = sqlx::query_scalar(r#"select string_agg(u.email, ',') as email from user_roles ur, roles r, users u where u.id = ur.user_id and r.id = ur.role_id and r.name = $1"#)
|
||||
.bind(role_name)
|
||||
.fetch_one(db_pool)
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@ use axum_extra::response::Html;
|
|||
use chrono::Utc;
|
||||
use http::StatusCode;
|
||||
use serde::Deserialize;
|
||||
use sqlx::{Row, SqlitePool};
|
||||
use sqlx::{Row, PgPool};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{
|
||||
middlewares::is_authorized,
|
||||
|
|
@ -47,7 +48,7 @@ struct WishListsTemplate {
|
|||
|
||||
pub async fn wishlists(
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
State(db_pool): State<PgPool>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
let logged_in = user_data.is_some();
|
||||
|
|
@ -93,8 +94,8 @@ struct UserWishListTemplate {
|
|||
}
|
||||
|
||||
pub async fn user_wishlist(
|
||||
Path(user_id): Path<i64>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
Path(user_id): Path<uuid::Uuid>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
|
|
@ -106,14 +107,15 @@ pub async fn user_wishlist(
|
|||
let userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
|
||||
|
||||
// Extract the user data.
|
||||
let person = sqlx::query_as!(UserData, "SELECT * FROM users WHERE id = ?", user_id)
|
||||
let person = sqlx::query_as("SELECT * FROM users WHERE id = $1")
|
||||
.bind(user_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
if is_authorized("/wishlist", user_data, db_pool.clone()).await {
|
||||
// Get user roles
|
||||
let user_roles = get_user_roles_display(userid, &db_pool.clone()).await;
|
||||
if is_authorized("/wishlist", user_data, db_pool.clone()).await {
|
||||
// Get user roles
|
||||
let user_roles = get_user_roles_display(userid, &db_pool.clone()).await;
|
||||
|
||||
// Get user wishlist
|
||||
let person_wishlist_items = get_user_wishlist_items(user_id, &db_pool.clone()).await;
|
||||
|
|
@ -150,8 +152,8 @@ struct UserWishListAddTemplate {
|
|||
}
|
||||
|
||||
pub async fn user_wishlist_add(
|
||||
Path(user_id): Path<i64>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
Path(user_id): Path<uuid::Uuid>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
|
|
@ -163,7 +165,8 @@ pub async fn user_wishlist_add(
|
|||
let userid = user_data.as_ref().map(|s| s.id.clone()).unwrap_or_default();
|
||||
|
||||
// Extract the user data.
|
||||
let person = sqlx::query_as!(UserData, "SELECT * FROM users WHERE id = ?", user_id)
|
||||
let person = sqlx::query_as("SELECT * FROM users WHERE id = $1")
|
||||
.bind(user_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
|
@ -199,19 +202,15 @@ pub struct ItemForm {
|
|||
}
|
||||
|
||||
pub async fn user_wishlist_add_item(
|
||||
Path(user_id): Path<i64>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
Path(user_id): Path<uuid::Uuid>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
Form(item_form): Form<ItemForm>,
|
||||
) -> impl IntoResponse {
|
||||
if is_authorized("/wishlist", user_data.clone(), db_pool.clone()).await {
|
||||
// Insert new item to database
|
||||
let now = Utc::now().timestamp();
|
||||
|
||||
sqlx::query("insert into wishlist_items (created_at, created_by, updated_at, updated_by, user_id, item, item_url) values (?, ?, ?, ?, ?, ?, ?)")
|
||||
.bind(now)// Created now
|
||||
sqlx::query("insert into wishlist_items (created_by, updated_by, user_id, item, item_url) values ($1, $2, $3, $4, $5)")
|
||||
.bind(user_data.as_ref().unwrap().id)// Created by current user
|
||||
.bind(now) // Updated now
|
||||
.bind(user_data.as_ref().unwrap().id) // Updated by current user
|
||||
.bind(user_id)
|
||||
.bind(item_form.item)
|
||||
|
|
@ -237,8 +236,8 @@ struct UserWishListEditTemplate {
|
|||
}
|
||||
|
||||
pub async fn user_wishlist_edit_item(
|
||||
Path(item_id): Path<i64>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
Path(item_id): Path<uuid::Uuid>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
// Is the user logged in?
|
||||
|
|
@ -274,16 +273,16 @@ pub async fn user_wishlist_edit_item(
|
|||
}
|
||||
|
||||
pub async fn user_wishlist_save_item(
|
||||
Path(item_id): Path<i64>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
Path(item_id): Path<uuid::Uuid>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
Form(item_form): Form<ItemForm>,
|
||||
) -> impl IntoResponse {
|
||||
if is_authorized("/wishlist", user_data.clone(), db_pool.clone()).await {
|
||||
// Insert new item to database
|
||||
let now = Utc::now().timestamp();
|
||||
let now = Utc::now().naive_local();
|
||||
|
||||
sqlx::query("update wishlist_items set updated_at = ?, updated_by = ?, item = ?, item_url = ? where id = ?")
|
||||
sqlx::query("update wishlist_items set updated_at = $1, updated_by = $2, item = $3, item_url = $4 where id = $5")
|
||||
.bind(now) // Updated now
|
||||
.bind(user_data.as_ref().unwrap().id) // Updated by current user
|
||||
.bind(item_form.item)
|
||||
|
|
@ -302,28 +301,28 @@ pub async fn user_wishlist_save_item(
|
|||
}
|
||||
|
||||
pub async fn user_wishlist_bought_item(
|
||||
Path(user_id): Path<i64>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
Path(item_id): Path<uuid::Uuid>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
if is_authorized("/wishlist", user_data.clone(), db_pool.clone()).await {
|
||||
// Update item to purchased
|
||||
sqlx::query("update wishlist_items set purchased_by = ? where id = ?")
|
||||
sqlx::query("update wishlist_items set purchased_by = $1 where id = $2")
|
||||
.bind(user_data.as_ref().unwrap().id) // Created by current user
|
||||
.bind(user_id)
|
||||
.bind(item_id)
|
||||
.execute(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Redirect to user wishlist
|
||||
// Extract the user data.
|
||||
let row = sqlx::query("SELECT user_id FROM wishlist_items WHERE id = ?")
|
||||
.bind(user_id)
|
||||
let row = sqlx::query("SELECT user_id FROM wishlist_items WHERE id = $1")
|
||||
.bind(item_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let userid = row.get::<i64, _>("user_id");
|
||||
let userid = row.get::<uuid::Uuid, _>("user_id");
|
||||
let redirect_string = format!("/userwishlist/{userid}");
|
||||
Redirect::to(&redirect_string).into_response()
|
||||
} else {
|
||||
|
|
@ -332,15 +331,15 @@ pub async fn user_wishlist_bought_item(
|
|||
}
|
||||
|
||||
pub async fn user_wishlist_received_item(
|
||||
Path(user_id): Path<i64>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
Path(user_id): Path<uuid::Uuid>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
if is_authorized("/wishlist", user_data.clone(), db_pool.clone()).await {
|
||||
// Update item received time
|
||||
let now = Utc::now().timestamp();
|
||||
let now = Utc::now().naive_local();
|
||||
|
||||
sqlx::query("update wishlist_items set received_at = ? where id = ?")
|
||||
sqlx::query("update wishlist_items set received_at = $1 where id = $2")
|
||||
.bind(now) // Received now
|
||||
.bind(user_id)
|
||||
.execute(&db_pool)
|
||||
|
|
@ -357,12 +356,12 @@ pub async fn user_wishlist_received_item(
|
|||
}
|
||||
|
||||
pub async fn user_wishlist_delete_item(
|
||||
Path(item_id): Path<i64>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
Path(item_id): Path<uuid::Uuid>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
if is_authorized("/wishlist", user_data.clone(), db_pool.clone()).await {
|
||||
sqlx::query("delete from wishlist_items where id = ?")
|
||||
sqlx::query("delete from wishlist_items where id = $1")
|
||||
.bind(item_id)
|
||||
.execute(&db_pool)
|
||||
.await
|
||||
|
|
@ -378,12 +377,12 @@ pub async fn user_wishlist_delete_item(
|
|||
}
|
||||
|
||||
pub async fn user_wishlist_returned_item(
|
||||
Path(item_id): Path<i64>,
|
||||
State(db_pool): State<SqlitePool>,
|
||||
Path(item_id): Path<uuid::Uuid>,
|
||||
State(db_pool): State<PgPool>,
|
||||
Extension(user_data): Extension<Option<UserData>>,
|
||||
) -> impl IntoResponse {
|
||||
if is_authorized("/wishlist", user_data.clone(), db_pool.clone()).await {
|
||||
sqlx::query("update wishlist_items set purchased_by = 0 where id = ?")
|
||||
sqlx::query("update wishlist_items set purchased_by = null where id = $1")
|
||||
.bind(item_id)
|
||||
.execute(&db_pool)
|
||||
.await
|
||||
|
|
@ -391,13 +390,13 @@ pub async fn user_wishlist_returned_item(
|
|||
|
||||
// Redirect to user wishlist
|
||||
// Extract the user data.
|
||||
let row = sqlx::query("SELECT user_id FROM wishlist_items WHERE id = ?")
|
||||
let row = sqlx::query("SELECT user_id FROM wishlist_items WHERE id = $1")
|
||||
.bind(item_id)
|
||||
.fetch_one(&db_pool)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let profileid = row.get::<i64, _>("user_id");
|
||||
let profileid = row.get::<Uuid, _>("user_id");
|
||||
let redirect_string = format!("/userwishlist/{profileid}");
|
||||
Redirect::to(&redirect_string).into_response()
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -25,38 +25,44 @@
|
|||
{% for person_wishlist_item in person_wishlist_items %}
|
||||
<tr>
|
||||
{% if my_wishlist %}
|
||||
<td><a href="/userwishlist/edit/{{ person_wishlist_item.id }}">{{ person_wishlist_item.item }}</a></td>
|
||||
<td><a href="/userwishlist/edit/{{ person_wishlist_item.id }}">{{ person_wishlist_item.item }}</a></td>
|
||||
{% else %}
|
||||
<td>{{ person_wishlist_item.item }}</td>
|
||||
<td>{{ person_wishlist_item.item }}</td>
|
||||
{% endif %}
|
||||
|
||||
{% if person_wishlist_item.item_url.len() > 0 %}
|
||||
<td><a href="{{ person_wishlist_item.item_url }}">URL</a></td>
|
||||
<td><a href="{{ person_wishlist_item.item_url }}">URL</a></td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
<td></td>
|
||||
{% endif %}
|
||||
|
||||
{% if person_wishlist_item.received_at > 0 %}
|
||||
<td>Got it!</td>
|
||||
{% else %}
|
||||
<td>Not yet!</td>
|
||||
{% endif %}
|
||||
|
||||
{% match person_wishlist_item.received_at %}
|
||||
{% when None %}
|
||||
<td>Not yet!</td>
|
||||
{% when Some with (received_at) %}
|
||||
<td>Got it!</td>
|
||||
{% endmatch %}
|
||||
|
||||
{% if my_wishlist %}
|
||||
{% if person_wishlist_item.received_at > 0 %}
|
||||
<td><a href="/userwishlist/delete/{{ person_wishlist_item.id }}">Delete</a></td>
|
||||
{% else %}
|
||||
<td><a href="/userwishlist/received/{{ person_wishlist_item.id }}">Received</a></td>
|
||||
{% endif %}
|
||||
{% match person_wishlist_item.received_at %}
|
||||
{% when None %}
|
||||
<td><a href="/userwishlist/received/{{ person_wishlist_item.id }}">Received</a></td>
|
||||
{% when Some with (received_at) %}
|
||||
<td><a href="/userwishlist/delete/{{ person_wishlist_item.id }}">Delete</a></td>
|
||||
{% endmatch %}
|
||||
{% else %}
|
||||
{% if person_wishlist_item.purchased_by == user.id %}
|
||||
<td><a href="/userwishlist/returned/{{ person_wishlist_item.id }}">Return</a></td>
|
||||
{% else if person_wishlist_item.purchased_by > 0 %}
|
||||
<td>Purchased</td>
|
||||
{% else %}
|
||||
<td><a href="/userwishlist/bought/{{ person_wishlist_item.id }}">Bought</a></td>
|
||||
{% endif %}
|
||||
{% match person_wishlist_item.purchased_by %}
|
||||
{% when Some with (purchased_by) %}
|
||||
{% if purchased_by.clone() == user.id %}
|
||||
<td><a href="/userwishlist/returned/{{ person_wishlist_item.id }}">Return</a></td>
|
||||
{% else %}
|
||||
<td>Purchased</td>
|
||||
{% endif %}
|
||||
{% when None %}
|
||||
<td><a href="/userwishlist/bought/{{ person_wishlist_item.id }}">Bought</a></td>
|
||||
{% endmatch %}
|
||||
{% endif %}
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
|
|||
Loading…
Reference in New Issue