From 5f6645f22a160e516d5196c62fd53cadeca4ec56 Mon Sep 17 00:00:00 2001 From: dvirlabs <114520947+dvirlabs@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:54:37 +0200 Subject: [PATCH] Update env for aws --- aws/.env | 13 +- .../__pycache__/email_utils.cpython-312.pyc | Bin 4101 -> 7794 bytes .../grocery_db_utils.cpython-312.pyc | Bin 12347 -> 13344 bytes backend/__pycache__/main.cpython-312.pyc | Bin 37720 -> 45991 bytes .../__pycache__/user_db_utils.cpython-312.pyc | Bin 5098 -> 5863 bytes backend/main.py | 2 +- backend/schema.sql | 154 +++++++++++++++++- 7 files changed, 158 insertions(+), 11 deletions(-) diff --git a/aws/.env b/aws/.env index 233b372..36f836f 100644 --- a/aws/.env +++ b/aws/.env @@ -2,7 +2,7 @@ DB_USER=recipes_user DB_PASSWORD=recipes_password DB_NAME=recipes_db -DB_HOST=my-recipes-rds.chw4omcqsuqv7.eu-central-1.rds.amazonaws.com +DB_HOST=my-recipes-rds.chw4omcguqv7.eu-central-1.rds.amazonaws.com DB_PORT=5432 # Email Configuration @@ -24,11 +24,12 @@ AZURE_CLIENT_SECRET=Zad8Q~qRBxaQq8up0lLXAq4pHzrVM2JFGFJhHaDp AZURE_TENANT_ID=consumers AZURE_REDIRECT_URI=http://localhost:8000/auth/azure/callback -# Cloudflare R2 Backup Configuration -R2_ENDPOINT=https://d4704b8c40b2f95b2c7bf7ee4ecc52f8.r2.cloudflarestorage.com -R2_ACCESS_KEY=1997b1e48a337c0dbe1f7552a08631b5 -R2_SECRET_KEY=369694e39fedfedb254158c147171f5760de84fa2346d5d5d5a961f1f517dbc6 -R2_BUCKET_NAME=recipes-backups +# AWS S3 Backup Configuration +S3_ENDPOINT=https://s3.eu-central-1.amazonaws.com +S3_ACCESS_KEY=1997b1e48a337c0dbe1f7552a08631b5 +S3_SECRET_KEY=369694e39fedfedb254158c147171f5760de84fa2346d5d5d5a961f1f517dbc6 +S3_BUCKET_NAME=recipes-backups +S3_REGION=eu-central-1 # Automatic Backup Schedule # Options: test (every 1 minute), daily, weekly, disabled diff --git a/backend/__pycache__/email_utils.cpython-312.pyc b/backend/__pycache__/email_utils.cpython-312.pyc index 6524825bc88ccabe7c01941e02c83613067ff80c..0f2969ffa10a832ee52eb1053722030c585242c2 100644 GIT binary patch delta 2794 zcmb7GO-vkD67C*mdj7iS2L=rIhZh{j49gfB7H2aIgxFT>M2_=gD^iHG)-bOJdKsqC z>mKYu?H!_cca_Tu)Sy6YBAg!?;~Wxk@gY$XDThdrB1j^&g>vvA+SO_gJ6^mOYY$u1 zJ>voAv@P}bs_IqMtE#T|UjK>wI~Dp)k^&q&|N8Pp<*U}YP&5Cxxx<-~!VQa5xXKMn zw2^w{5cR?9mqS^B2CjOB!!#&IP?mL5@v3_`TIH!E*Mm+7WDT}m80;dpT@+-ostoMv zfp4TZFWG(?fNw%R>!SV`Fnfx8yy>&=f6w#b^@Qsa7yr=Qa-ZTKuD5xzZvK|}Xa6z4 zJN{+9-F!W8!jptF8rGKs|K&mao_HeI4+0iN7&8AZc6v?%`RaO;6yw{5s#6!@b?gK( z9)#xrK+tc#5c-YhG%#klXa3)^5bMmNfUV@t>jn?Y*`~h6T%*ZW`q`mR)oC()6%oH`9-^M18g72 z3JCyf)>^VYvKFix#JU0G4eNbto+Jnir8VmsOss%(nOL{1>(&z6j}8wYw4sMP009Dz zJ-uKpciEgHx(SAJV6|c`5Fl=X&O8Br$BOei*0f!`cckW^1iJ599~0{X>pHRK!NC&f zELf|^%vrMp!ZWQU$K^+uz^t`~bG1A=tTizE5L6PqM+s=(1pBpk(q5#nP>tS+cNWux z@G#gV&=hZ>JB$~cn*smvsG~Or^Q*A-d{@ByTsSNgu0RU~%=^M#GvXZ%rN(*&YdM;R zx22St9JHhV|ErPeo*cAS#Ui7d-6gpOp{+S3xei}!5exk5qLg-5CAr%vRi=oh7jyDJ z`$U?JD)}VoJvw=r9PKB%d|6MVbIK^lGBWHm*56(W2LeiyF(zjR+6#Y$zHcXwBE@K{ zPDj$E%=jo%3wfHzs5zA-$-$nUOkbbePb&PSF+~S%GEJ$HA5D^DJ+M9o(f^oB+b?nA zqH;!MXwJ6iWqlkISo>Q2|nlj?@b+N^oom+ITyz>nH8rRi{njSlW; z&s29sb|hd9`5VogZ^-=A_q6#>U$dv$i^Dp(m9 zindxbJejM<*G0#db84N1`8?Vt%nt(NZTDhic5r3?YGHMBBYJ%1#Rmcx-naPDkD<<) z(|23WnEQgQ$J|21Qx-WPGIO@f2~kg35=Gz4sq#K=BvuY_jcv23G8ZA9a(EBQAM}{h z!I4PdRE+;d&Hn^1mN>&j>#nDVT~0ZNfIb&@*X}Kb?zb8r%O58{shreuqii^j=04L1;z^#aQ~7Yo)I)&dOLAr^I2T@b87;-xcdZI0>-j9#PdC!(SwZo6GJJyRvo!Ohg$r zhuvQfeW?kR7IN|+djn*cgZ7>IPPkLJ+uXXrHJa<;GjDEl^?an>&OJD3C@c delta 327 zcmexl)2hICnwOW00SNw@+hr#4PUMqdTr^QV*(HUog&~S3g+GNol{bY0h&fYv)7VnD zT3Dm_Qn*w3L1JkvDLgGKQ35c23U8_)Pz@hYOsJAkQ(&_YV;mzR-{wN5HB5|EljT@l z7^^lXv!<~yYEC}IX$U0$aL#2^m^_QiiPa3~1fk6jx&AN$h41k=ahn4LiUdJ~^<-^c zEmk`qGhlNRuLz^OHB2f*lex$i#9;*yHb4R>U_QB*Pn*>V$cdP|moHgVld(t+BqLm; z22us`Y?1clcQQVzV6FThJ|~C(IiW}cM1btN#gdetpHt*Cxk6T!%N;1d2*kxDljqB7 TO+GK{!S#hfh*6-37bpk-zAZ*O diff --git a/backend/__pycache__/grocery_db_utils.cpython-312.pyc b/backend/__pycache__/grocery_db_utils.cpython-312.pyc index 6a656035a86706ea5b01b95e0a80c585fbb771be..75e7239c8e1ec05a66618ab396c94ff3a26e45a3 100644 GIT binary patch delta 742 zcmZ8eO-xfk5bm~p?c?>m{y=|#{;;4>8Yvzm(TgG^7{y8qh(s=J<8EywZOOh86niop zK&#N~fs+P2X}pj;qec%Tnivu zD3S@}%f@hyc2=Fu=y5fA9JN-nc@(U^?MET*N72J>DiIHL6Ome|l{%=9F}FaLp&5}h z&08m%hzv0js7T!z8}WkA%M&Q5_yGAxnE0uK1o#LF*lnbFUYPV(*V`Ie;4yF&#Uk1> zbh{Ou=BGkkow&&G%rr@tD5h6(YAIK^gw<@CQ9MmqKBua=VnNl4p(%tO@DJe=(g^f% z15~}v(~WI>I5MkP;JPa|z9hDDIe1bqmrZ5FF9Z|evcMcAWjCTMJVZOVfQY4m{|*lD zAHg$y*14YXzm1SwX-|2afgaO^|N3KHK*ARebnv?&dsxDIWNhdhzL+i~sljs;ck*=9 z%SUmH-|{H@m(9sn!pHhYST_)`1RSajj-7ng2Jn4gXCle^Ad>$E!Op_#nvt<+e16C! z__jsIl4H?PGYg8lCYzl86G zuHEKM#k*fxr4+#Lpz53YYFgr-P&5A-pK3LBR_~n|l+55@MjlJ?x!$mp0cfm5 zeWk7Sx=>#e@~w{5Okkd>b+bw8MxRLPDWcP?emSqx{8p;4HBtO(#MOHE`oWuj0REQB AkpKVy delta 284 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 Hei-L=R{Xo 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 Xer00|&c_^h9xL$Ql1YZW1crYB4ZM`N diff --git a/backend/main.py b/backend/main.py index 2a792fb..f520815 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1231,4 +1231,4 @@ def trigger_restore( if __name__ == "__main__": - uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True) \ No newline at end of file + uvicorn.run("main:app", host="0.0.0.0", port=8001, reload=True) \ No newline at end of file diff --git a/backend/schema.sql b/backend/schema.sql index 5b61e17..a432695 100644 --- a/backend/schema.sql +++ b/backend/schema.sql @@ -8,11 +8,13 @@ CREATE TABLE IF NOT EXISTS users ( last_name TEXT, display_name TEXT UNIQUE NOT NULL, is_admin BOOLEAN DEFAULT FALSE, + auth_provider VARCHAR(50) DEFAULT 'local' NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_users_username ON users (username); CREATE INDEX IF NOT EXISTS idx_users_email ON users (email); +CREATE INDEX IF NOT EXISTS idx_users_auth_provider ON users (auth_provider); -- Create recipes table CREATE TABLE IF NOT EXISTS recipes ( @@ -20,13 +22,15 @@ CREATE TABLE IF NOT EXISTS recipes ( name TEXT NOT NULL, meal_type TEXT NOT NULL, -- breakfast / lunch / dinner / snack time_minutes INTEGER NOT NULL, - tags TEXT[] NOT NULL DEFAULT '{}', -- {"מהיר", "בריא"} - ingredients TEXT[] NOT NULL DEFAULT '{}', -- {"ביצה", "עגבניה", "מלח"} - steps TEXT[] NOT NULL DEFAULT '{}', -- {"לחתוך", "לבשל", ...} + tags JSONB NOT NULL DEFAULT '[]', -- ["מהיר", "בריא"] + ingredients JSONB NOT NULL DEFAULT '[]', -- ["ביצה", "עגבניה", "מלח"] + steps JSONB NOT NULL DEFAULT '[]', -- ["לחתוך", "לבשל", ...] image TEXT, -- Base64-encoded image or image URL made_by TEXT, -- Person who created this recipe version user_id INTEGER REFERENCES users(id) ON DELETE SET NULL, -- Recipe owner - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + visibility VARCHAR(20) DEFAULT 'public', -- public / private / friends / groups + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT recipes_visibility_check CHECK (visibility IN ('public', 'private', 'friends', 'groups')) ); -- Optional: index for filters @@ -81,6 +85,148 @@ CREATE TABLE IF NOT EXISTS notifications ( CREATE INDEX IF NOT EXISTS idx_notifications_user_id ON notifications (user_id); CREATE INDEX IF NOT EXISTS idx_notifications_is_read ON notifications (is_read); +-- Create friend requests table +CREATE TABLE IF NOT EXISTS friend_requests ( + id SERIAL PRIMARY KEY, + sender_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + receiver_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + status VARCHAR(20) DEFAULT 'pending', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT friend_requests_check CHECK (sender_id <> receiver_id), + CONSTRAINT friend_requests_status_check CHECK (status IN ('pending', 'accepted', 'rejected')), + UNIQUE(sender_id, receiver_id) +); + +CREATE INDEX IF NOT EXISTS idx_friend_requests_sender_id ON friend_requests (sender_id); +CREATE INDEX IF NOT EXISTS idx_friend_requests_receiver_id ON friend_requests (receiver_id); +CREATE INDEX IF NOT EXISTS idx_friend_requests_status ON friend_requests (status); + +-- Create friendships table +CREATE TABLE IF NOT EXISTS friendships ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + friend_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT friendships_check CHECK (user_id <> friend_id), + UNIQUE(user_id, friend_id) +); + +CREATE INDEX IF NOT EXISTS idx_friendships_user_id ON friendships (user_id); +CREATE INDEX IF NOT EXISTS idx_friendships_friend_id ON friendships (friend_id); + +-- Create groups table +CREATE TABLE IF NOT EXISTS groups ( + id SERIAL PRIMARY KEY, + name VARCHAR(100) NOT NULL, + description TEXT, + created_by INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + is_private BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX IF NOT EXISTS idx_groups_created_by ON groups (created_by); + +-- Create group members table +CREATE TABLE IF NOT EXISTS group_members ( + id SERIAL PRIMARY KEY, + group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE CASCADE, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + role VARCHAR(20) DEFAULT 'member', + joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT group_members_role_check CHECK (role IN ('admin', 'moderator', 'member')), + UNIQUE(group_id, user_id) +); + +CREATE INDEX IF NOT EXISTS idx_group_members_group_id ON group_members (group_id); +CREATE INDEX IF NOT EXISTS idx_group_members_user_id ON group_members (user_id); + +-- Create conversations table +CREATE TABLE IF NOT EXISTS conversations ( + id SERIAL PRIMARY KEY, + name VARCHAR(100), + is_group BOOLEAN DEFAULT FALSE, + created_by INTEGER REFERENCES users(id) ON DELETE SET NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +); + +CREATE INDEX IF NOT EXISTS idx_conversations_created_by ON conversations (created_by); + +-- Create conversation members table +CREATE TABLE IF NOT EXISTS conversation_members ( + id SERIAL PRIMARY KEY, + conversation_id INTEGER NOT NULL REFERENCES conversations(id) ON DELETE CASCADE, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + last_read_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(conversation_id, user_id) +); + +CREATE INDEX IF NOT EXISTS idx_conversation_members_conversation_id ON conversation_members (conversation_id); +CREATE INDEX IF NOT EXISTS idx_conversation_members_user_id ON conversation_members (user_id); + +-- Create messages table +CREATE TABLE IF NOT EXISTS messages ( + id SERIAL PRIMARY KEY, + conversation_id INTEGER NOT NULL REFERENCES conversations(id) ON DELETE CASCADE, + sender_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + content TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + edited_at TIMESTAMP, + is_deleted BOOLEAN DEFAULT FALSE +); + +CREATE INDEX IF NOT EXISTS idx_messages_conversation_id ON messages (conversation_id); +CREATE INDEX IF NOT EXISTS idx_messages_sender_id ON messages (sender_id); +CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages (created_at); + +-- Create recipe shares table +CREATE TABLE IF NOT EXISTS recipe_shares ( + id SERIAL PRIMARY KEY, + recipe_id INTEGER NOT NULL REFERENCES recipes(id) ON DELETE CASCADE, + group_id INTEGER NOT NULL REFERENCES groups(id) ON DELETE CASCADE, + shared_by INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + shared_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE(recipe_id, group_id) +); + +CREATE INDEX IF NOT EXISTS idx_recipe_shares_recipe_id ON recipe_shares (recipe_id); +CREATE INDEX IF NOT EXISTS idx_recipe_shares_group_id ON recipe_shares (group_id); +CREATE INDEX IF NOT EXISTS idx_recipe_shares_shared_by ON recipe_shares (shared_by); + +-- Create recipe ratings table +CREATE TABLE IF NOT EXISTS recipe_ratings ( + id SERIAL PRIMARY KEY, + recipe_id INTEGER NOT NULL REFERENCES recipes(id) ON DELETE CASCADE, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + rating INTEGER NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT recipe_ratings_rating_check CHECK (rating >= 1 AND rating <= 5), + UNIQUE(recipe_id, user_id) +); + +CREATE INDEX IF NOT EXISTS idx_recipe_ratings_recipe_id ON recipe_ratings (recipe_id); +CREATE INDEX IF NOT EXISTS idx_recipe_ratings_user_id ON recipe_ratings (user_id); + +-- Create recipe comments table +CREATE TABLE IF NOT EXISTS recipe_comments ( + id SERIAL PRIMARY KEY, + recipe_id INTEGER NOT NULL REFERENCES recipes(id) ON DELETE CASCADE, + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + content TEXT NOT NULL, + parent_comment_id INTEGER REFERENCES recipe_comments(id) ON DELETE CASCADE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + is_deleted BOOLEAN DEFAULT FALSE +); + +CREATE INDEX IF NOT EXISTS idx_recipe_comments_recipe_id ON recipe_comments (recipe_id); +CREATE INDEX IF NOT EXISTS idx_recipe_comments_user_id ON recipe_comments (user_id); +CREATE INDEX IF NOT EXISTS idx_recipe_comments_parent_comment_id ON recipe_comments (parent_comment_id); + -- Create default admin user (password: admin123) -- Password hash generated with bcrypt for 'admin123' INSERT INTO users (username, email, password_hash, first_name, last_name, display_name, is_admin)