Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 55 additions & 52 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -111,20 +111,21 @@ model mfa_challenges {
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model mfa_factors {
id String @id @db.Uuid
user_id String @db.Uuid
friendly_name String?
factor_type factor_type
status factor_status
created_at DateTime @db.Timestamptz(6)
updated_at DateTime @db.Timestamptz(6)
secret String?
phone String?
last_challenged_at DateTime? @unique @db.Timestamptz(6)
web_authn_credential Json?
web_authn_aaguid String? @db.Uuid
mfa_challenges mfa_challenges[]
users auth_users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
id String @id @db.Uuid
user_id String @db.Uuid
friendly_name String?
factor_type factor_type
status factor_status
created_at DateTime @db.Timestamptz(6)
updated_at DateTime @db.Timestamptz(6)
secret String?
phone String?
last_challenged_at DateTime? @unique @db.Timestamptz(6)
web_authn_credential Json?
web_authn_aaguid String? @db.Uuid
last_webauthn_challenge_data Json?
mfa_challenges mfa_challenges[]
users auth_users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction)

@@unique([user_id, phone], map: "unique_phone_factor_per_user")
@@index([user_id, created_at], map: "factor_id_created_at_idx")
Expand Down Expand Up @@ -285,22 +286,24 @@ model schema_migrations {
/// This model or at least one of its fields has comments in the database, and requires an additional setup for migrations: Read more: https://pris.ly/d/database-comments
/// This model contains row level security and requires additional setup for migrations. Visit https://pris.ly/d/row-level-security for more info.
model sessions {
id String @id @db.Uuid
user_id String @db.Uuid
created_at DateTime? @db.Timestamptz(6)
updated_at DateTime? @db.Timestamptz(6)
factor_id String? @db.Uuid
aal aal_level?
not_after DateTime? @db.Timestamptz(6)
refreshed_at DateTime? @db.Timestamp(6)
user_agent String?
ip String? @db.Inet
tag String?
oauth_client_id String? @db.Uuid
mfa_amr_claims mfa_amr_claims[]
refresh_tokens refresh_tokens[]
oauth_clients oauth_clients? @relation(fields: [oauth_client_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
users auth_users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
id String @id @db.Uuid
user_id String @db.Uuid
created_at DateTime? @db.Timestamptz(6)
updated_at DateTime? @db.Timestamptz(6)
factor_id String? @db.Uuid
aal aal_level?
not_after DateTime? @db.Timestamptz(6)
refreshed_at DateTime? @db.Timestamp(6)
user_agent String?
ip String? @db.Inet
tag String?
oauth_client_id String? @db.Uuid
refresh_token_hmac_key String?
refresh_token_counter BigInt?
mfa_amr_claims mfa_amr_claims[]
refresh_tokens refresh_tokens[]
oauth_clients oauth_clients? @relation(fields: [oauth_client_id], references: [id], onDelete: Cascade, onUpdate: NoAction)
users auth_users @relation(fields: [user_id], references: [id], onDelete: Cascade, onUpdate: NoAction)

@@index([not_after(sort: Desc)])
@@index([oauth_client_id])
Expand Down Expand Up @@ -397,24 +400,24 @@ model auth_users {
}

model Comment {
id String @id @default(uuid())
userId String @db.Uuid
ratingId String?
postId String?
content String
createdAt DateTime @default(now())
parentId String?
parent_comment Comment? @relation("CommentToComment", fields: [parentId], references: [id], onDelete: SetNull)
child_comment Comment[] @relation("CommentToComment")
Post Post? @relation(fields: [postId], references: [id])
Rating Rating? @relation(fields: [ratingId], references: [id])
UserProfile UserProfile @relation(fields: [userId], references: [userId])
id String @id
userId String @db.Uuid
ratingId String?
postId String?
content String
createdAt DateTime @default(now())
parentId String?
Comment Comment? @relation("CommentToComment", fields: [parentId], references: [id])
other_Comment Comment[] @relation("CommentToComment")
Post Post? @relation(fields: [postId], references: [id])
Rating Rating? @relation(fields: [ratingId], references: [id])
UserProfile UserProfile @relation(fields: [userId], references: [userId])

@@schema("public")
}

model Post {
id String @id @default(uuid())
id String @id
userId String @db.Uuid
content String
type PostType
Expand All @@ -427,7 +430,7 @@ model Post {
}

model Rating {
id String @id @default(uuid())
id String @id
userId String @db.Uuid
movieId String
stars Int
Expand Down Expand Up @@ -455,16 +458,16 @@ model UserFollow {
model UserProfile {
userId String @id @db.Uuid
username String?
favoriteMovies String[] @default([])
createdAt DateTime @default(now())
updatedAt DateTime
city String?
country String?
favoriteGenres String[] @default([])
onboardingCompleted Boolean @default(false)
primaryLanguage String @default("English")
secondaryLanguage String[] @default([])
profilePicture String?
country String?
city String?
favoriteGenres String[] @default([])
favoriteMovies String[] @default([])
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
secondaryLanguage String[] @default([])
Comment Comment[]
Post Post[]
Rating Rating[]
Expand All @@ -484,7 +487,7 @@ model bootcamp {
}

model local_event {
id String @id(map: "local_events_pkey") @default(uuid()) @db.Uuid
id String @id(map: "local_events_pkey") @db.Uuid
title String
time DateTime? @default(dbgenerated("(now() AT TIME ZONE 'utc'::text)")) @db.Timestamptz(6)
description String
Expand Down
99 changes: 99 additions & 0 deletions backend/src/controllers/translate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { Request, Response } from 'express';

const BASE_URL = 'https://ftapi.pythonanywhere.com';

interface TranslationResponse {
'source-language': string;
'source-text': string;
'destination-language': string;
'destination-text': string;
pronunciation: {
'source-text-phonetic': string | null;
'source-text-audio': string;
'destination-text-audio': string;
};
translations: {
'all-translations': Array<[string, string[]]> | null;
'possible-translations': string[];
'possible-mistakes': string[] | null;
};
definitions: any[] | null;
'see-also': string[] | null;
}

/**
* translate text with optional source language (auto detect if not provided)
* @query text - Text to translate (required)
* @query dl - Destination language code (required)
* @query sl - Source language code (optional, auto detects if not provided)
*/
export const translateText = async (req: Request, res: Response) => {
try {
const { text, dl, sl } = req.query;

if (!text || !dl) {
return res.status(400).json({
error: 'Missing parameters',
message: 'Both text and destination language are required',
});
}

const params = new URLSearchParams({
dl: dl as string,
text: text as string,
});

if (sl) {
params.append('sl', sl as string);
}

const response = await fetch(`${BASE_URL}/translate?${params.toString()}`);

if (!response.ok) {
throw new Error(`API error: ${response.statusText}`);
}

const data: TranslationResponse = await response.json();

return res.status(200).json({
success: true,
sourceLanguage: data['source-language'],
sourceText: data['source-text'],
destinationLanguage: data['destination-language'],
destinationText: data['destination-text'],
pronunciation: data.pronunciation,
translations: data.translations,
definitions: data.definitions,
});
} catch (error) {
console.error('Translation error:', error);
return res.status(500).json({
error: 'Translation failed',
message: error instanceof Error ? error.message : 'Unknown error occurred',
});
}
};

// get all supported languages
export const getSupportedLanguages = async (req: Request, res: Response) => {
try {
const response = await fetch(`${BASE_URL}/languages`);

if (!response.ok) {
throw new Error(`API error: ${response.statusText}`);
}

const languages = await response.json();

return res.status(200).json({
success: true,
languages,
});
} catch (error) {
console.error('Error fetching languages:', error);
return res.status(500).json({
error: 'Failed to fetch supported languages',
message: error instanceof Error ? error.message : 'Unknown error occurred',
});
}
};
4 changes: 4 additions & 0 deletions backend/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
getMovieById,
updateMovie,
} from "../controllers/tmdb";
import { translateText, getSupportedLanguages } from "../controllers/translate";
import { deleteUserProfile, ensureUserProfile, getUserComments, getUserProfile, getUserRatings, updateUserProfile } from '../controllers/user';
import { authenticateUser } from '../middleware/auth';
import { protect } from "../controllers/protected";
Expand All @@ -28,6 +29,9 @@ router.get("/swagger-output.json", serveSwagger);
//OpenAPI 3.0 spec
router.get("/openapi.json", serveSwagger);

router.get("/api/translate", translateText);
router.get("/api/languages", getSupportedLanguages);

// everything under here is a private endpoint
router.use('/api', authenticateUser, ensureUserProfile);

Expand Down
Loading