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
3 changes: 3 additions & 0 deletions tenders-finder/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
VITE_SUPABASE_PROJECT_ID="dksfgbuuciwhicmpdkys"
VITE_SUPABASE_PUBLISHABLE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRrc2ZnYnV1Y2l3aGljbXBka3lzIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkyNTE5NDIsImV4cCI6MjA4NDgyNzk0Mn0.vgKY9Gc5qtZOP5ZZOzGvKihqLgWXrwMSbTYYpSCFpEg"
VITE_SUPABASE_URL="https://dksfgbuuciwhicmpdkys.supabase.co"
Comment on lines +1 to +3
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove committed secrets from .env and rotate the exposed key.
The publishable key (JWT) is in version control, which can enable unauthorized access. Replace with placeholders, add .env to .gitignore, and rotate/revoke the exposed key in Supabase.

🔒 Proposed fix (keep only placeholders in repo)
-VITE_SUPABASE_PROJECT_ID="dksfgbuuciwhicmpdkys"
-VITE_SUPABASE_PUBLISHABLE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImRrc2ZnYnV1Y2l3aGljbXBka3lzIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjkyNTE5NDIsImV4cCI6MjA4NDgyNzk0Mn0.vgKY9Gc5qtZOP5ZZOzGvKihqLgWXrwMSbTYYpSCFpEg"
-VITE_SUPABASE_URL="https://dksfgbuuciwhicmpdkys.supabase.co"
+VITE_SUPABASE_PROJECT_ID=""
+VITE_SUPABASE_PUBLISHABLE_KEY=""
+VITE_SUPABASE_URL=""
🧰 Tools
🪛 dotenv-linter (4.0.0)

[warning] 1-1: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 2-2: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)


[warning] 3-3: [QuoteCharacter] The value has quote characters (', ")

(QuoteCharacter)

🪛 Gitleaks (8.30.0)

[high] 2-2: Uncovered a JSON Web Token, which may lead to unauthorized access to web applications and sensitive user data.

(jwt)

🤖 Prompt for AI Agents
In `@tenders-finder/.env` around lines 1 - 3, The committed .env contains leaked
secrets (VITE_SUPABASE_PROJECT_ID, VITE_SUPABASE_PUBLISHABLE_KEY,
VITE_SUPABASE_URL); remove these real values and replace them with placeholder
values in the file, add .env to .gitignore, and rotate/revoke the exposed
publishable key in Supabase immediately; ensure any deployment or CI uses secure
secret storage (environment variables or secret manager) and update
documentation to show using placeholders for VITE_SUPABASE_PROJECT_ID,
VITE_SUPABASE_PUBLISHABLE_KEY, and VITE_SUPABASE_URL.

24 changes: 24 additions & 0 deletions tenders-finder/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
127 changes: 127 additions & 0 deletions tenders-finder/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Project Title - Government Tender Finder for Singapore

**Live Link**: https://tender-scout-singapore.lovable.app


## What This Project Is -
This project is an AI-powered summer school discovery and comparison tool that automatically finds, scans, and extracts information from official summer school websites worldwide.
Instead of relying on outdated lists or manual searches, the app pulls live data directly from source websites and returns it in a clean, structured format.

## What This Project Is -

**AI-based Link Discovery**

The system first uses an AI layer to identify and curate relevant summer school websites based on the user’s query (region, subject, age group, duration, etc.).

**Automated Web Browsing with TinyFish**
The curated links are passed to the TinyFish Web Agent API, which launches multiple browser agents in parallel to:

1) Navigate dynamic pages

2) Handle real websites (no static scraping)

3) Extract structured details such as program name, location, dates, eligibility, fees, and deadlines

**Real-Time Streaming & Aggregation**
Progress updates are streamed via SSE, and extracted results are normalised into a unified JSON format for easy comparison.

## What to Expect

1) Live, up-to-date data pulled directly from official websites

2) Parallel web scanning for fast results

3) Real-time status updates during execution

4) Structured, comparable output (JSON)


**Demo Video** - https://drive.google.com/file/d/1GXZhJOjiVUP5XcGvTAvRGcYhTWoKXlsE/view?usp=sharing


## Code snippet -
```bash
const response = await fetch("https://mino.ai/v1/automation/run-sse", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": "sk-mino-YOUR_API_KEY",
},
body: JSON.stringify({
url: "https://www.gebiz.gov.sg",
goal: "Extract the latest open government tenders. Return JSON with tenderTitle, agency, tenderID, procurementCategory, submissionDeadline, eligibilityCriteria, estimatedValue, tenderStatus, and tenderLink.",
browser_profile: "lite",
}),
});

const reader = response.body!.getReader();
const decoder = new TextDecoder();

while (true) {
const { done, value } = await reader.read();
if (done) break;

const chunk = decoder.decode(value);
for (const line of chunk.split("\n")) {
if (line.startsWith("data: ")) {
const data = JSON.parse(line.slice(6));

// Live browser view
if (data.streamingUrl) {
console.log("Live view:", data.streamingUrl);
}

// Final structured output
if (data.type === "COMPLETE" && data.resultJson) {
console.log("Extracted tenders:", data.resultJson);
}
}
}
}
```


## Tech Stack
**Next.js (TypeScript)**

**Mino API**

**AI**

## Architecture Diagram
```mermaid
flowchart TB

%% =======================
%% UI LAYER
%% =======================
UI["USER INTERFACE<br/>(React + Tailwind + Lovable)"]

%% =======================
%% ORCHESTRATION
%% =======================
ORCH["Tender Search Orchestration Layer<br/>(Next.js API / Server Actions)"]

%% =======================
%% SERVICES
%% =======================
DB["SUPABASE<br/>(Cached Tenders & Metadata)"]
MINO["MINO API<br/>(Browser Automation)"]

%% =======================
%% DETAILS
%% =======================
DBD["• Cached tender listings<br/>• Deduplicated tenders<br/>• Historical records"]
MINOD["• Parallel web agents<br/>• Browse govt tender portals<br/>• Open tender pages<br/>• Extract structured fields<br/>• SSE streaming updates"]

%% =======================
%% CONNECTIONS
%% =======================
UI --> ORCH

ORCH --> DB
ORCH --> MINO

DB --> DBD
MINO --> MINOD
```
Binary file added tenders-finder/bun.lockb
Binary file not shown.
20 changes: 20 additions & 0 deletions tenders-finder/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "src/index.css",
"baseColor": "slate",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}
26 changes: 26 additions & 0 deletions tenders-finder/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import js from "@eslint/js";
import globals from "globals";
import reactHooks from "eslint-plugin-react-hooks";
import reactRefresh from "eslint-plugin-react-refresh";
import tseslint from "typescript-eslint";

export default tseslint.config(
{ ignores: ["dist"] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ["**/*.{ts,tsx}"],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
"react-hooks": reactHooks,
"react-refresh": reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
"@typescript-eslint/no-unused-vars": "off",
},
},
);
26 changes: 26 additions & 0 deletions tenders-finder/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<!-- TODO: Set the document title to the name of your application -->
<title>Lovable App</title>
<meta name="description" content="Lovable Generated Project" />
<meta name="author" content="Lovable" />

<!-- TODO: Update og:title to match your application name -->
<meta property="og:title" content="Lovable App" />
<meta property="og:description" content="Lovable Generated Project" />
<meta property="og:type" content="website" />
<meta property="og:image" content="https://lovable.dev/opengraph-image-p98pqg.png" />

<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:site" content="@Lovable" />
<meta name="twitter:image" content="https://lovable.dev/opengraph-image-p98pqg.png" />
</head>

<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Loading