Skip to content

This is open-source E-Commerce project - feel free to pick up any task from 'Projects' - then create fork - then create PR

Notifications You must be signed in to change notification settings

nicitaacom/23_store

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

23-store-overview




Project info

Performance - 35 mobile / 73 desktop (01.03.2024)

Performance - 60 mobile / ? desktop (10.03.2024)

Performance - 74 mobile / 94 desktop (24.03.2024)

Performance - 65 mobile / 99 desktop (15.07.2024)

perf

Stack - Next + TypeScript + Tailwind + supabase + zustand + stripe




Clone repository

Step 1.1 - clone repository (variant 1)

alt text

or Step 1.1 - clone repository (variant 2)

git clone https://github.com/nicitaacom/23_store
cd 23_store

Step 1.2 - install deps

pnpm i



Step 2 - setup .env (variant 1 - dockerized)

Step 2.1 Prerequirements - install docker

Commands (also available with video guide on YouTube - https://www.youtube.com/watch?v=O2D6rPJI2oM)

sudo apt update
sudo apt intall -y docker.io
sudo systemctl enable docker --now
sudo usermod -aG docker $USER
sudo reboot

Step 2.2 - build docker image and run docker container

docker build -t 23_store .
docker run -dp 3000:3000 23_store

To stop docker use - docker stop <container_id_or_name>

Step 2 - setup .env (variant 2 - manually)

2.1 - google cloud console

setup_google_video

2.2 - supabase

Login in supabase - https://app.supabase.com/sign-in Login in supabase

2.3 - supabase

Click new project

2.4 - supabase

Enter aer

2.5 - supabase

Set up supabase project

2.6 - supabase

Copy .env

2.7 - supabase

Paste .env

2.8 - supabase setup

1. messages
create table
  public.messages (
    id uuid not null default gen_random_uuid (),
    created_at timestamp with time zone not null default now(),
    ticket_id text not null,
    sender_id text not null,
    sender_username text not null,
    body text not null,
    images text[] null,
    seen boolean not null default false,
    sender_avatar_url text null,
    constraint messages_pkey primary key (id),
    constraint messages_ticket_id_fkey foreign key (ticket_id) references tickets (id)
  ) tablespace pg_default;

Allow select for SUPPORT and ADMIN roles

((( SELECT users.role
   FROM users
  WHERE (users.id = auth.uid())) = 'SUPPORT'::text) OR (( SELECT users.role
   FROM users
  WHERE (users.id = auth.uid())) = 'ADMIN'::text))
2. products
create table
  public.products (
    price_id character varying not null,
    title character varying not null,
    sub_title character varying not null,
    price numeric not null,
    img_url character varying[] not null,
    on_stock integer not null,
    owner_id uuid not null,
    id character varying not null,
    constraint products_pkey primary key (price_id, owner_id, id),
    constraint products_owner_id_fkey foreign key (owner_id) references auth.users (id) on update cascade on delete cascade
  ) tablespace pg_default;

Allow delete for owner_id

(owner_id = auth.uid())

insert for authenticated users

Target roles - authenticated

true

select for all users

true

update for product owners

(owner_id = auth.uid())
3. tickets
create table
  public.tickets (
    created_at timestamp with time zone not null default now(),
    is_open boolean not null default true,
    owner_username text not null,
    owner_id text not null,
    id text not null,
    last_message_body text not null default ''::text,
    owner_avatar_url text null,
    rate integer null,
    constraint tickets_pkey primary key (id)
  ) tablespace pg_default;

allow close ticket for SUPPORT and ADMIN roles

((( SELECT users.role
   FROM users
  WHERE (users.id = auth.uid())) = 'SUPPORT'::text) OR (( SELECT users.role
   FROM users
  WHERE (users.id = auth.uid())) = 'ADMIN'::text))

allow select for SUPPORT and ADMIN

((( SELECT users.role
   FROM users
  WHERE (users.id = auth.uid())) = 'SUPPORT'::text) OR (( SELECT users.role
   FROM users
  WHERE (users.id = auth.uid())) = 'ADMIN'::text))
4. users
create table
  public.users (
    id uuid not null,
    created_at timestamp with time zone not null default now(),
    username text not null,
    email text not null,
    avatar_url text null,
    role text not null default 'USER'::text,
    email_confirmed_at timestamp with time zone null,
    providers text[] null default '{}'::text[],
    constraint users_pkey primary key (id),
    constraint users_id_fkey foreign key (id) references auth.users (id) on update cascade on delete cascade
  ) tablespace pg_default;

allow select for users based on their ids

(id = auth.uid())
5. users_cart
create table
  public.users_cart (
    id uuid not null,
    created_at timestamp with time zone not null default now(),
    cart_products jsonb not null default '{}'::jsonb,
    constraint users_cart1_pkey primary key (id),
    constraint users_cart_id_fkey foreign key (id) references auth.users (id) on update cascade on delete cascade
  ) tablespace pg_default;

allow select based on their id

(id = auth.uid())

allow user to update their carts

(id = auth.uid())
Authentication - Providers

Email - enabled

  • Confirm email - ON
  • Secure email change - ON
  • Secure password change - OFF
  • Mailer OTP Expiration - 86400
  • Min password length - 6

Google - enabled

  • Client ID (for OAuth) - paste from .env
  • Client Secret (for OAuth) - paste from .env

Twitter - enabled

API key - paste from .env API Secret Key - paste from .env

Authentication - Email templates

Adjust another emails for Invite user Magic link Change email address Reset password if needed

Confirm signup

<table style="max-width: 640px; width: 100%;background-color:rgb(32,32,32)" align="center">
  <td style="min-width:100%;margin:0rem;padding:1rem 0rem;text-align:center"></td>

  <tr>
    <td style="text-align:center">
      <img src="https://i.imgur.com/KmMEBux.png" alt="" style="width: 40%;" />
    </td>
  </tr>
  <tr>
    <td style="font-weight: bold; text-align:center;font-size: 18px; color: #666666; padding: 10px 0;">
      Verify your email on 23_store
    </td>
  </tr>
  <tr>
    <td style="text-align: center;">
      <a
        href="{{ .ConfirmationURL }}"
        style="display: inline-block; background-color: #4CAF50; color: #1f1f1f; padding: 10px 20px; text-align: center; text-decoration: none; border-radius: 4px; cursor: pointer; font-weight: bold;">
        Verify email
      </a>
    </td>
  </tr>

  <table style="border-top:1px solid #999999;min-width:100%;margin:1rem 0rem;padding:1rem 0rem;text-align:center">
    <tbody>
      <tr>
        <td>
          <a
            href="{{ .SiteURL }}/support"
            style="color:rgb(64,125,237);text-decoration:none;margin:0px;font-size:0.875rem;line-height:1.25rem;text-align:center;margin-right:1rem"
            target="_blank"
            data-saferedirecturl="https://www.google.com/url?q={{ .SiteURL }}/support&amp;source=gmail&amp;ust=1696683582414000&amp;usg=AOvVaw1cLGx1tiGtSp3MUWJAOiih"
            >Support</a
          >
          <a
            href="{{ .SiteURL }}/feedback"
            style="color:rgb(64,125,237);text-decoration:none;margin:0px;font-size:0.875rem;line-height:1.25rem;text-align:center;margin-right:1rem"
            target="_blank"
            data-saferedirecturl="https://www.google.com/url?q={{ .SiteURL }}/feedback&amp;source=gmail&amp;ust=1696683582414000&amp;usg=AOvVaw2J2syDW1hX-6J6kkisMOBZ"
            >Feedback</a
          >
        </td>
      </tr>
    </tbody>
  </table>
</table>
Authentication - URL Configuration

Site URL - https://23-store.vercel.app

Redirect URLs: (note that its better to don't use ** - use path without ** instead)

http://localhost:3023/\*\* (I hate prettier)

https://23-store.vercel.app/auth/callback/credentials

https://23-store.vercel.app/?modal=AuthModal&variant=resetPassword&code=**

https://23-store.vercel.app/error?error_description=**

https://23-store.vercel.app/auth/completed?code=**

https://23-store.vercel.app/**

https://23-store.vercel.app/auth/callback/oauth?provider=**
Storage
  1. Create new bucked called 'public' and make 'public bucket' - ON

  2. Create RLS policy for that bucket

Allow select and instert for all users

(bucket_id = 'public'::text)

I noticed only here that only authenticated users may instert so you may adjust this RLS policy if you want

2.9 - stripe - login/register

stripe - https://app.supabase.com/sign-in

login/register in stripe

2.10 - stripe

copy .env

2.11 - stripe

paste .env

2.12 - resend - login/register

login/register in resend.com

2.13 - resend - buy your domain

buy and add domain in resend

2.14 - resend

copy .env

2.15 - resend

Alternativaly if you have problems on this step you may check guide on YouTube how to setup resend or how to send email using react

2.16 - your email you send emails from

NEXT_PUBLIC_SUPPORT_EMAIL='your_email_where_you_send_messages_from'

It may be your email based on your domain like [email protected]

2.17 - paypal

  1. Google - paypal developer - login/register
  2. Use this guide - https://developer.paypal.com/api/rest/ copy .env

2.18 - paypal

  1. Click 'Create App' - in case you haven't one
  2. Click on your app name in my case 'Platform Partner App - 5321855133911008505' copy .env

2.19 - paypal

copy .env

2.20 - paypal

copy .env

2.21 - metamask

  1. I suppose that you advanced PC user and able to register/login in metamask copy metamask address

2.22 - metamsk

paste metamask address

2.23 - coinmarketcap

  1. login/register in coinmarketcap developer (google - coinmarketcap developer - login - etc)

copy .env

2.24 - coinmarketcap

paste .env

Step 2.25 - run project

pnpm dev



Feedback

If you found some bug/issue - just go ahead and open a new issue