From 0b204161ce0b4bbefae9dccaef66bde09d0ef1de Mon Sep 17 00:00:00 2001 From: Sarthak Date: Mon, 8 Sep 2025 23:59:29 +0530 Subject: [PATCH 1/6] feat(recommendations): implement personalized podcasts recommendations with daily refresh --- .../com/example/podify/PodifyApplication.java | 2 + .../com/example/podify/dto/PodcastDTO.java | 3 ++ .../com/example/podify/model/Podcast.java | 3 ++ .../jpa/WatchHistoryRepository.java | 1 + .../repository/mongo/PodcastRepository.java | 6 ++- .../services/impl/PodcastRefreshService.java | 41 +++++++++++++++++++ .../services/impl/PodcastServiceImpl.java | 23 ++++++++--- .../services/impl/YouTubeServiceImpl.java | 21 ++++++---- 8 files changed, 86 insertions(+), 14 deletions(-) create mode 100644 server/src/main/java/com/example/podify/services/impl/PodcastRefreshService.java diff --git a/server/src/main/java/com/example/podify/PodifyApplication.java b/server/src/main/java/com/example/podify/PodifyApplication.java index 23fbc4f..c014b4c 100644 --- a/server/src/main/java/com/example/podify/PodifyApplication.java +++ b/server/src/main/java/com/example/podify/PodifyApplication.java @@ -4,8 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling @EnableJpaRepositories(basePackages = "com.example.podify.repository.jpa") @EnableMongoRepositories(basePackages = "com.example.podify.repository.mongo") public class PodifyApplication { diff --git a/server/src/main/java/com/example/podify/dto/PodcastDTO.java b/server/src/main/java/com/example/podify/dto/PodcastDTO.java index 5431ad5..27e8276 100644 --- a/server/src/main/java/com/example/podify/dto/PodcastDTO.java +++ b/server/src/main/java/com/example/podify/dto/PodcastDTO.java @@ -5,6 +5,8 @@ import lombok.Data; import lombok.RequiredArgsConstructor; +import java.time.Instant; + @Data @AllArgsConstructor @RequiredArgsConstructor @@ -17,4 +19,5 @@ public class PodcastDTO { private String topicName; // name of the topic private String description; private String videoUrl; + private Instant lastFetchedAt; } diff --git a/server/src/main/java/com/example/podify/model/Podcast.java b/server/src/main/java/com/example/podify/model/Podcast.java index 6b76c58..2f1bd8c 100644 --- a/server/src/main/java/com/example/podify/model/Podcast.java +++ b/server/src/main/java/com/example/podify/model/Podcast.java @@ -12,6 +12,7 @@ import org.springframework.data.mongodb.core.index.CompoundIndexes; import org.springframework.data.mongodb.core.mapping.Document; +import java.time.Instant; import java.util.UUID; @Document(collection = "podcasts") @@ -41,4 +42,6 @@ public class Podcast extends Auditable { private String videoUrl; private String transcript; + + private Instant lastFetchedAt; } diff --git a/server/src/main/java/com/example/podify/repository/jpa/WatchHistoryRepository.java b/server/src/main/java/com/example/podify/repository/jpa/WatchHistoryRepository.java index ef7d300..4f4583e 100644 --- a/server/src/main/java/com/example/podify/repository/jpa/WatchHistoryRepository.java +++ b/server/src/main/java/com/example/podify/repository/jpa/WatchHistoryRepository.java @@ -4,6 +4,7 @@ import com.example.podify.model.User; import com.example.podify.model.WatchHistory; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import java.util.List; diff --git a/server/src/main/java/com/example/podify/repository/mongo/PodcastRepository.java b/server/src/main/java/com/example/podify/repository/mongo/PodcastRepository.java index b474f18..7bd55c1 100644 --- a/server/src/main/java/com/example/podify/repository/mongo/PodcastRepository.java +++ b/server/src/main/java/com/example/podify/repository/mongo/PodcastRepository.java @@ -4,6 +4,7 @@ import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; +import java.time.Instant; import java.util.List; @EnableMongoRepositories @@ -12,6 +13,7 @@ public interface PodcastRepository extends MongoRepository { // fetch first N podcast for a topic List findTop3ByTopicNameOrderByCreatedAtDesc(String topicName); - // fetch by multiple topics - List findByTopicNameIn(List topicNames); + // Get podcasts updated after a cutoff time + List findByTopicNameAndLastFetchedAtAfterOrderByLastFetchedAtDesc(String topicName, Instant cutoff); + } diff --git a/server/src/main/java/com/example/podify/services/impl/PodcastRefreshService.java b/server/src/main/java/com/example/podify/services/impl/PodcastRefreshService.java new file mode 100644 index 0000000..69a9cd9 --- /dev/null +++ b/server/src/main/java/com/example/podify/services/impl/PodcastRefreshService.java @@ -0,0 +1,41 @@ +package com.example.podify.services.impl; +import com.example.podify.model.Topic; +import com.example.podify.model.User; +import com.example.podify.repository.jpa.UserRepository; +import com.example.podify.services.Signable; +import com.example.podify.services.WatchHistoryService; +import com.example.podify.services.YouTubeService; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; +import java.util.List; + +@Service +public class PodcastRefreshService extends Signable { + + private final YouTubeService youTubeService; + private final WatchHistoryService watchHistoryService; + + public PodcastRefreshService(UserRepository userRepository, YouTubeService youTubeService, WatchHistoryService watchHistoryService) { + super(userRepository); + this.youTubeService = youTubeService; + this.watchHistoryService = watchHistoryService; + } + + // Every day new podcasts are fetched + @Scheduled(fixedRate = 1000 * 60 * 60 * 24) + public void refreshPodcasts() { + User user = getLoggedInUser(); + + List topics = user.getTopics(); + for (Topic topic : topics) { + String topicName = topic.getName(); + try { + youTubeService.fetchAndSavePodcasts(topicName); + } catch (Exception e) { + System.err.println("Error fetching podcasts for topic " + topicName + ": " + e.getMessage()); + } + } + } +} + diff --git a/server/src/main/java/com/example/podify/services/impl/PodcastServiceImpl.java b/server/src/main/java/com/example/podify/services/impl/PodcastServiceImpl.java index f966dca..637d1d7 100644 --- a/server/src/main/java/com/example/podify/services/impl/PodcastServiceImpl.java +++ b/server/src/main/java/com/example/podify/services/impl/PodcastServiceImpl.java @@ -12,6 +12,8 @@ import com.example.podify.services.YouTubeService; import org.springframework.stereotype.Service; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.*; @Service @@ -41,11 +43,16 @@ public Map> getPodcastsByTopics() { List list = podcastRepository.findTop3ByTopicNameOrderByCreatedAtDesc(topicName); // if podcast aren't in db - if (list.isEmpty()) { - // then fetched from yt - List fetched = youTubeService.fetchAndSavePodcasts(topicName); - - res.put(topicName, fetched); + if (list.isEmpty() || isStale(list)) { + try { + // then fetched from yt + List fetched = youTubeService.fetchAndSavePodcasts(topicName); + res.put(topicName, fetched); + } catch (Exception e) { + System.err.println("Error fetching podcasts for topic " + topicName + ": " + e.getMessage()); + List dtos = list.stream().map(PodcastMapper::toDTO).toList(); + res.put(topicName, dtos); + } } else { // if it is in DB then send that List dtos = list.stream().map(PodcastMapper::toDTO).toList(); @@ -55,6 +62,12 @@ public Map> getPodcastsByTopics() { return res; } + private boolean isStale(List podcasts) { + if (podcasts.isEmpty()) return true; + Instant latest = podcasts.getFirst().getLastFetchedAt(); + return latest.isBefore(Instant.now().minus(24, ChronoUnit.HOURS)); + } + @Override public Optional getById(String id) { return podcastRepository.findById(id).map(PodcastMapper::toDTO); diff --git a/server/src/main/java/com/example/podify/services/impl/YouTubeServiceImpl.java b/server/src/main/java/com/example/podify/services/impl/YouTubeServiceImpl.java index 8b9fd6f..8deb887 100644 --- a/server/src/main/java/com/example/podify/services/impl/YouTubeServiceImpl.java +++ b/server/src/main/java/com/example/podify/services/impl/YouTubeServiceImpl.java @@ -13,6 +13,7 @@ import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; +import java.time.Instant; import java.util.List; import java.util.Map; import java.util.Objects; @@ -85,14 +86,20 @@ public List fetchAndSavePodcasts(String topicName) { } }).filter(Objects::nonNull).toList(); - // Save only if podcast doesn't exist - List newPodcasts = podcastDTOS.stream() - .filter(p -> !podcastRepository.existsById(p.getId())) - .map(PodcastMapper::toEntity) - .toList(); + Instant now = Instant.now(); - if (!newPodcasts.isEmpty()) { - podcastRepository.saveAll(newPodcasts); + + for (PodcastDTO dto : podcastDTOS) { + podcastRepository.findById(dto.getId()).ifPresentOrElse(existingPodcast -> { + // Update existing podcast's lastFetchedAt + existingPodcast.setLastFetchedAt(now); + podcastRepository.save(existingPodcast); + }, () -> { + // Create new podcast + Podcast newPodcast = PodcastMapper.toEntity(dto); + newPodcast.setLastFetchedAt(now); + podcastRepository.save(newPodcast); + }); } return podcastDTOS; From 8a372186fa17f6d210f82b62e22bcb9476be579f Mon Sep 17 00:00:00 2001 From: Sarthak Date: Tue, 9 Sep 2025 00:12:06 +0530 Subject: [PATCH 2/6] fix some issue --- client/app/dashboard/page.tsx | 2 +- .../podify/repository/jpa/UserRepository.java | 5 +++++ .../podify/services/impl/PodcastRefreshService.java | 13 +++++-------- .../podify/services/impl/PodcastServiceImpl.java | 2 ++ .../podify/services/impl/YouTubeServiceImpl.java | 1 - 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/client/app/dashboard/page.tsx b/client/app/dashboard/page.tsx index 845bc82..e721248 100644 --- a/client/app/dashboard/page.tsx +++ b/client/app/dashboard/page.tsx @@ -310,7 +310,7 @@ export default function Dashboard() {
- {podcasts[topic].map((podcast) => ( + {podcasts[topic].slice(0,3).map((podcast) => ( { Optional findByEmail(String email); + @Query("SELECT u FROM User u LEFT JOIN FETCH u.topics") + List findAllWithTopics(); + } diff --git a/server/src/main/java/com/example/podify/services/impl/PodcastRefreshService.java b/server/src/main/java/com/example/podify/services/impl/PodcastRefreshService.java index 69a9cd9..ee59b12 100644 --- a/server/src/main/java/com/example/podify/services/impl/PodcastRefreshService.java +++ b/server/src/main/java/com/example/podify/services/impl/PodcastRefreshService.java @@ -25,15 +25,12 @@ public PodcastRefreshService(UserRepository userRepository, YouTubeService youTu // Every day new podcasts are fetched @Scheduled(fixedRate = 1000 * 60 * 60 * 24) public void refreshPodcasts() { - User user = getLoggedInUser(); + List users = userRepository.findAllWithTopics(); // fetch all users - List topics = user.getTopics(); - for (Topic topic : topics) { - String topicName = topic.getName(); - try { - youTubeService.fetchAndSavePodcasts(topicName); - } catch (Exception e) { - System.err.println("Error fetching podcasts for topic " + topicName + ": " + e.getMessage()); + for (User user : users) { + List topics = user.getTopics(); + for (Topic topic : topics) { + youTubeService.fetchAndSavePodcasts(topic.getName()); } } } diff --git a/server/src/main/java/com/example/podify/services/impl/PodcastServiceImpl.java b/server/src/main/java/com/example/podify/services/impl/PodcastServiceImpl.java index 637d1d7..978357e 100644 --- a/server/src/main/java/com/example/podify/services/impl/PodcastServiceImpl.java +++ b/server/src/main/java/com/example/podify/services/impl/PodcastServiceImpl.java @@ -65,6 +65,8 @@ public Map> getPodcastsByTopics() { private boolean isStale(List podcasts) { if (podcasts.isEmpty()) return true; Instant latest = podcasts.getFirst().getLastFetchedAt(); + // if lastFetchedAt is null, consider it stale + if (latest == null) return true; return latest.isBefore(Instant.now().minus(24, ChronoUnit.HOURS)); } diff --git a/server/src/main/java/com/example/podify/services/impl/YouTubeServiceImpl.java b/server/src/main/java/com/example/podify/services/impl/YouTubeServiceImpl.java index 8deb887..a291261 100644 --- a/server/src/main/java/com/example/podify/services/impl/YouTubeServiceImpl.java +++ b/server/src/main/java/com/example/podify/services/impl/YouTubeServiceImpl.java @@ -88,7 +88,6 @@ public List fetchAndSavePodcasts(String topicName) { Instant now = Instant.now(); - for (PodcastDTO dto : podcastDTOS) { podcastRepository.findById(dto.getId()).ifPresentOrElse(existingPodcast -> { // Update existing podcast's lastFetchedAt From caa9dd5d4956e851a21c45a5e1e5037052eb655a Mon Sep 17 00:00:00 2001 From: Sarthak Date: Tue, 9 Sep 2025 14:29:06 +0530 Subject: [PATCH 3/6] implement github workflows --- .github/workflows/backend-dev.yml | 39 ++++++++++++++++++ .github/workflows/backend-prod.yml | 42 +++++++++++++++++++ .github/workflows/backend-staging.yml | 39 ++++++++++++++++++ .github/workflows/frontend-dev.yml | 57 ++++++++++++++++++++++++++ .github/workflows/frontend-prod.yml | 50 ++++++++++++++++++++++ .github/workflows/frontend-staging.yml | 50 ++++++++++++++++++++++ 6 files changed, 277 insertions(+) create mode 100644 .github/workflows/backend-dev.yml create mode 100644 .github/workflows/backend-prod.yml create mode 100644 .github/workflows/backend-staging.yml create mode 100644 .github/workflows/frontend-dev.yml create mode 100644 .github/workflows/frontend-prod.yml create mode 100644 .github/workflows/frontend-staging.yml diff --git a/.github/workflows/backend-dev.yml b/.github/workflows/backend-dev.yml new file mode 100644 index 0000000..6d1991f --- /dev/null +++ b/.github/workflows/backend-dev.yml @@ -0,0 +1,39 @@ +name: Backend CI - Dev + +on: + push: + branches: ["dev"] + pull_request: + branches: ["dev"] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up JDK 22 + uses: actions/setup-java@v3 + with: + java-version: 22 + distribution: temurin + + - name: Cache Maven packages + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Build & Test with Maven + run: mvn clean install -DskipTests + working-directory: ./server + + - name: Upload JAR Artifact + uses: actions/upload-artifact@v4 + with: + name: backend-jar + path: ./server/target/*.jar diff --git a/.github/workflows/backend-prod.yml b/.github/workflows/backend-prod.yml new file mode 100644 index 0000000..2b48d7e --- /dev/null +++ b/.github/workflows/backend-prod.yml @@ -0,0 +1,42 @@ +name: Backend CI - Prod + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up JDK 22 + uses: actions/setup-java@v3 + with: + java-version: 22 + distribution: temurin + + - name: Cache Maven packages + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Build & Test with Maven + run: mvn clean install -DskipTests + working-directory: ./server + + - name: Upload JAR Artifact + uses: actions/upload-artifact@v4 + with: + name: backend-jar + path: ./server/target/*.jar + + - name: Deploy to Production + run: echo "🚀 Deploying backend to production..." diff --git a/.github/workflows/backend-staging.yml b/.github/workflows/backend-staging.yml new file mode 100644 index 0000000..1bc8c58 --- /dev/null +++ b/.github/workflows/backend-staging.yml @@ -0,0 +1,39 @@ +name: Backend CI - Staging + +on: + push: + branches: ["staged"] + pull_request: + branches: ["staged"] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v3 + + - name: Set up JDK 22 + uses: actions/setup-java@v3 + with: + java-version: 22 + distribution: temurin + + - name: Cache Maven packages + uses: actions/cache@v3 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- + + - name: Build & Test with Maven + run: mvn clean install -DskipTests + working-directory: ./server + + - name: Upload JAR Artifact + uses: actions/upload-artifact@v4 + with: + name: backend-jar + path: ./server/target/*.jar diff --git a/.github/workflows/frontend-dev.yml b/.github/workflows/frontend-dev.yml new file mode 100644 index 0000000..396f347 --- /dev/null +++ b/.github/workflows/frontend-dev.yml @@ -0,0 +1,57 @@ +name: Frontend CI - Dev + +on: + push: + branches: ["dev"] + pull_request: + branches: ["dev"] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Cache node modules + uses: actions/cache@v3 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install dependencies + run: npm install --legacy-peer-deps + working-directory: ./client + + - name: Build Next.js app + run: npm run build + working-directory: ./client + + # To ensure code quality and catch bugs before merging: + - name: Run Linter + run: npm run lint + working-directory: ./client + + - name: Run TypeScript check + run: npm run type-check + working-directory: ./client + if: success() + + - name: Run Tests + run: npm test -- --watchAll=false + working-directory: ./client + + - name: Upload build artifact + if: success() + uses: actions/upload-artifact@v4 + with: + name: client-build + path: ./client/.next diff --git a/.github/workflows/frontend-prod.yml b/.github/workflows/frontend-prod.yml new file mode 100644 index 0000000..8463394 --- /dev/null +++ b/.github/workflows/frontend-prod.yml @@ -0,0 +1,50 @@ +name: Frontend CI - Prod + +on: + push: + branches: ["main"] + pull_request: + branches: ["main"] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Cache node modules + uses: actions/cache@v3 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install dependencies + run: npm install --legacy-peer-deps + working-directory: ./client + + - name: Build Next.js app + run: npm run build + working-directory: ./client + + - name: Run Linter + run: npm run lint + working-directory: ./client + + - name: Run Tests + run: npm test -- --watchAll=false + working-directory: ./client + + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: client-build + path: ./client/.next diff --git a/.github/workflows/frontend-staging.yml b/.github/workflows/frontend-staging.yml new file mode 100644 index 0000000..457da37 --- /dev/null +++ b/.github/workflows/frontend-staging.yml @@ -0,0 +1,50 @@ +name: Frontend CI - Staging + +on: + push: + branches: ["staged"] + pull_request: + branches: ["staged"] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Cache node modules + uses: actions/cache@v3 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + + - name: Install dependencies + run: npm install --legacy-peer-deps + working-directory: ./client + + - name: Build Next.js app + run: npm run build + working-directory: ./client + + - name: Run Linter + run: npm run lint + working-directory: ./client + + - name: Run Tests + run: npm test -- --watchAll=false + working-directory: ./client + + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: client-build + path: ./client/.next From 0cbf7382662cedb839c5b3064e9dace7722f7ff1 Mon Sep 17 00:00:00 2001 From: Sarthak Date: Tue, 9 Sep 2025 14:37:03 +0530 Subject: [PATCH 4/6] fix(error): added id in topic page --- client/component/TopicPage.tsx | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/client/component/TopicPage.tsx b/client/component/TopicPage.tsx index d061c65..577d6b7 100644 --- a/client/component/TopicPage.tsx +++ b/client/component/TopicPage.tsx @@ -8,38 +8,58 @@ import { useSession } from "next-auth/react"; import { fetchWithToken } from "@/lib/fetchWithToken"; const topicsList: Topic[] = [ - { name: "Technology", emoji: "💻", description: "AI, gadgets, innovation" }, - { name: "Health", emoji: "🏥", description: "Wellness, fitness, medicine" }, + { + name: "Technology", + emoji: "💻", + description: "AI, gadgets, innovation", + id: "tech", + }, + { + name: "Health", + emoji: "🏥", + description: "Wellness, fitness, medicine", + id: "health", + }, { name: "Business", emoji: "💼", description: "Entrepreneurship, finance, leadership", + id: "business", }, { name: "Education", emoji: "📚", description: "Learning, teaching, academia", + id: "edu", + }, + { + name: "Entertainment", + emoji: "🎭", + description: "Movies, music, culture", + id: "entertainment", }, - { name: "Entertainment", emoji: "🎭", description: "Movies, music, culture" }, { name: "Sports", emoji: "⚽", description: "Athletics, competitions, teams", + id: "sports", }, { name: "Science", emoji: "🔬", description: "Research, discoveries, experiments", + id: "sci", }, { name: "History", emoji: "📜", description: "Past events, civilizations, stories", + id: "hist", }, ]; const TopicPage = () => { - const { status } = useSession(); + const { status } = useSession(); const [selectedTopics, setSelectedTopics] = useState([]); const router = useRouter(); const [loading, setLoading] = useState(true); From a3d4f1d2b904389789473c61619016a9828c0f0a Mon Sep 17 00:00:00 2001 From: Sarthak Date: Tue, 9 Sep 2025 14:59:47 +0530 Subject: [PATCH 5/6] chore: added the dummy env files for client and server --- .gitignore | 3 +- client/.env.example | 9 +++++ .../resources/application.properties.example | 33 +++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 client/.env.example create mode 100644 server/src/main/resources/application.properties.example diff --git a/.gitignore b/.gitignore index 42d8ed9..0c69dac 100644 --- a/.gitignore +++ b/.gitignore @@ -16,8 +16,7 @@ Thumbs.db #################################### # Environment & Security Files #################################### -*.env -.env.* +client/.env .env.local .env.development.local .env.test.local diff --git a/client/.env.example b/client/.env.example new file mode 100644 index 0000000..c82788d --- /dev/null +++ b/client/.env.example @@ -0,0 +1,9 @@ +# Google oAuth Login +GOOGLE_CLIENT_ID=your-google-client-id +GOOGLE_CLIENT_SECRET=your-client-secret + +NEXTAUTH_URL=your-frontend-url + +NEXT_PUBLIC_SPRING_BASE_URL=http://your-backend-url/api + +NEXTAUTH_SECRET = your-secret-key \ No newline at end of file diff --git a/server/src/main/resources/application.properties.example b/server/src/main/resources/application.properties.example new file mode 100644 index 0000000..f353bef --- /dev/null +++ b/server/src/main/resources/application.properties.example @@ -0,0 +1,33 @@ +spring.application.name=podify + +# ===== Database (PostgreSQL) Configuration ===== +spring.datasource.url=jdbc:postgresql://localhost:5432/your-db-name +spring.datasource.username=your-db-usernmae +spring.datasource.password=your-db-password +spring.datasource.driver-class-name=org.postgresql.Driver +spring.jpa.hibernate.ddl-auto=update +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.properties.hibernate.format_sql=true + + +# =============================== +# MongoDB Config +# =============================== +spring.data.mongodb.uri=mongodb://localhost:27017/your-mongodb-name + +# ===== OAuth Client Configuration ===== +# Google OAuth +spring.security.oauth2.client.registration.google.client-id=your-google-client-id +spring.security.oauth2.client.registration.google.client-secret=your-google-client-secret +spring.security.oauth2.client.registration.google.redirect-uri=your-google-redirect-uri +spring.security.oauth2.client.registration.google.scope=your-google-api-scope +spring.security.oauth2.client.registration.google.authorization-grant-type=your-google-authorization-type + +next.auth.secret=your-next-auth-secret + +client.url=your-frontend-url + +base.url=your-backend-url + +#Youtube +youtube.api.key=your-yt-api-key \ No newline at end of file From 65a1b6090feab1a6d2cc321584e0be49ed18a99e Mon Sep 17 00:00:00 2001 From: Sarthak Date: Mon, 22 Sep 2025 09:47:47 +0530 Subject: [PATCH 6/6] fix(signout): signout handle issue --- client/component/Sidebar.tsx | 8 ++++++-- client/lib/middleware.ts | 4 ++-- server/start-server.sh | 16 ++++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 server/start-server.sh diff --git a/client/component/Sidebar.tsx b/client/component/Sidebar.tsx index 162017a..bc2c3da 100644 --- a/client/component/Sidebar.tsx +++ b/client/component/Sidebar.tsx @@ -68,8 +68,12 @@ const Sidebar = ({ }, [sidebarOpen]); function handleSignOut(event: MouseEvent): void { - signOut(); - router.push("/"); + event.preventDefault(); + + signOut({ + redirect: true, + callbackUrl: "/home", + }); } return ( diff --git a/client/lib/middleware.ts b/client/lib/middleware.ts index 500efcd..131092f 100644 --- a/client/lib/middleware.ts +++ b/client/lib/middleware.ts @@ -7,5 +7,5 @@ export default withAuth({ }); export const config = { - matcher : ['/topics', '/dashboard'] -} + matcher: ["/topics", "/dashboard"], +}; diff --git a/server/start-server.sh b/server/start-server.sh new file mode 100644 index 0000000..935d700 --- /dev/null +++ b/server/start-server.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# Check if port 8080 is in use +PID=$(lsof -ti:8080) + +if [ -n "$PID" ]; then + echo "Port 8080 is in use by process $PID. Killing it..." + sudo kill -9 $PID + echo "Process $PID killed." +else + echo "Port 8080 is free." +fi + +# Now start the Spring Boot application +echo "Starting Spring Boot application..." +mvn spring-boot::run # or 'java -jar target/your-app.jar'