feat: connect backend with the frontend

This commit is contained in:
2025-07-23 15:02:32 +00:00
parent 7abed7e634
commit 98369ef531
11 changed files with 54 additions and 20 deletions

View File

@@ -1,3 +1,6 @@
# Backend environment variables #
# Must be a PostgreSQL db connection string
DATABASE_URL=postgresql://user:password@localhost:5432/mydatabase
# [dev, stage, prod]
@@ -15,4 +18,7 @@ HASHIDS_SALT=default-insecure-salt
# random.shuffle(base61)
# print("".join(base61))
###
ENCODER_ALPHABET=CnArvIseYhld2BtZipybguVKaMx4QFkcR71DTLJEP65jUGzqmw9fSoXW83HNO
ENCODER_ALPHABET=CnArvIseYhld2BtZipybguVKaMx4QFkcR71DTLJEP65jUGzqmw9fSoXW83HNO
### which origins are allowed to make cross origin requests (CORS)
ALLOW_ORIGINS=http://localhost:3000,http://localhost:3030

View File

@@ -1,10 +1,15 @@
{
"recommendations": [
// Javascript
"dbaeumer.vscode-eslint",
// Python
"ms-python.python",
"ms-python.debugpy",
"ms-python.pylint",
// SQL
"mtxr.sqltools",
"mtxr.sqltools-driver-pg",
// Misc
"humao.rest-client",
"genieai.chatgpt-vscode"
],

View File

@@ -1,15 +1,20 @@
from pydantic_settings import BaseSettings
from typing import Literal
from typing import Literal, List
class Settings(BaseSettings):
database_url: str
environment: Literal["dev", "stage", "prod"]
hashids_salt: str
encoder_alphabet: str
allow_origins: str
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
def get_allow_origins(self) -> List[str]:
return [origin.strip() for origin in self.allow_origins.split(",")]
# Singleton instance of settings
settings = Settings()

View File

@@ -1,4 +1,5 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from backend.config import settings
from backend.db import database, Base, engine
@@ -6,6 +7,14 @@ from backend.routes import shortener_router
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=settings.get_allow_origins(),
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.on_event("startup")
async def startup():
await database.connect()

4
frontend/.env.example Normal file
View File

@@ -0,0 +1,4 @@
# Frontend environment variables #
REACT_APP_API_BASE_URL=http://localhost:8000
REACT_APP_BASE_URL=http://localhost:8000

View File

@@ -17,6 +17,7 @@
"@types/node": "^16.18.126",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"eslint": "^8.57.1",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-redux": "^9.2.0",

View File

@@ -12,6 +12,7 @@
"@types/node": "^16.18.126",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"eslint": "^8.57.1",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-redux": "^9.2.0",

View File

@@ -1,8 +1,9 @@
export interface ShortenUrlRequest {
longUrl: string;
url: string;
}
export interface ShortenUrlResponse {
shortCode: string;
url: string;
shortcode: string;
}

View File

@@ -1,11 +1,11 @@
import { ShortenUrlRequest, ShortenUrlResponse } from "./types";
const baseUrl = process.env.REACT_APP_API_BASE_URL
export async function shortenUrlApi(
payload: ShortenUrlRequest
): Promise<ShortenUrlResponse> {
/* TODO
const response = await fetch('https://api.minxa.lol/api/v1/url', {
const response = await fetch(`${baseUrl}/`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
@@ -14,10 +14,11 @@ export async function shortenUrlApi(
if (!response.ok) {
throw new Error('Failed to shorten URL');
}
*/
const data = await response.json()
return {
shortCode: 'Ux5vy' // Dummy value return
url: data.url,
shortcode: data.shortcode
}
}

View File

@@ -5,9 +5,10 @@ import { UrlState } from './types';
export const shortenUrl = createAsyncThunk(
'url/shortenUrl',
async (longUrl: string) => {
const data = await shortenUrlApi({ longUrl });
return `https://minxa.lol/${data.shortCode}`;
async (url: string) => {
const baseUrl = process.env.REACT_APP_BASE_URL
const data = await shortenUrlApi({ url });
return `${baseUrl}/${data.shortcode}`;
}
);

View File

@@ -11,7 +11,7 @@ import {
const Home: React.FC = () => {
/* component level state */
const [longUrl, setLongUrl] = useState('');
const [url, setUrl] = useState('');
/* global state */
const dispatch = useAppDispatch();
@@ -22,8 +22,8 @@ const Home: React.FC = () => {
/* methods */
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === 'Enter' && longUrl.trim() !== '') {
dispatch(shortenUrl(longUrl));
if (e.key === 'Enter' && url.trim() !== '') {
dispatch(shortenUrl(url));
}
};
@@ -42,9 +42,9 @@ const Home: React.FC = () => {
<input
type="text"
placeholder="Enter your long URL here"
value={longUrl}
onChange={(e) => setLongUrl(e.target.value)}
placeholder="https://example.com/my-long-url"
value={url}
onChange={(e) => setUrl(e.target.value)}
onKeyDown={handleKeyDown}
className="w-80 p-3 rounded-md text-lg border border-gray-300 shadow-sm focus:outline-none focus:ring-2 focus:ring-orange-400"
/>
@@ -77,7 +77,7 @@ const Home: React.FC = () => {
onClick={handleCopy}
className="bg-orange-500 text-white px-4 py-2 rounded hover:bg-orange-600 transition">
Copy to Clipboard
</button>
</button>
</div>
)}
</div>