Skip to content

Commit f217998

Browse files
🎯 First commit
0 parents  commit f217998

11 files changed

Lines changed: 249 additions & 0 deletions

File tree

.env.sample

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Environment sample
2+
3+
# Admin Login
4+
ADMIN_USER=Admin
5+
ADMIN_PASS=Pink2222
6+
7+
# Telegram Bot
8+
TELEGRAM_BOT_TOKEN=your_telegram_bot_token
9+
ADMIN_ID=your_numeric_telegram_user_id
10+
PROMO_FEED_URL=https://your-app.fly.dev/api/feed

.github/workflows/deploy.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Deploy to Fly.io
2+
3+
on:
4+
push:
5+
branches: [main]
6+
7+
jobs:
8+
deploy:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v3
12+
- name: Set up Node
13+
uses: actions/setup-node@v3
14+
with:
15+
node-version: 18
16+
17+
- name: Install backend dependencies
18+
run: cd backend && npm install
19+
20+
- name: Install frontend dependencies
21+
run: cd frontend && npm install && npm run build
22+
23+
- name: Install Fly CLI
24+
uses: superfly/flyctl-actions/setup-flyctl@master
25+
26+
- name: Deploy to Fly.io
27+
run: flyctl deploy --remote-only
28+
env:
29+
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# GambleCodez Fullstack Bot (SQLite)
2+
3+
✅ SQLite backend (no cloud)
4+
✅ React + Tailwind frontend
5+
✅ Telegram bot with /links + /send
6+
7+
To deploy:
8+
1. Set Fly.io secrets from `.env.sample`
9+
2. Push to GitHub → GitHub Actions deploy to Fly.io
10+
11+
Backend auto-creates SQLite tables.

backend/index.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
2+
const express = require("express");
3+
const cors = require("cors");
4+
const path = require("path");
5+
const sqlite3 = require("sqlite3").verbose();
6+
7+
const app = express();
8+
const PORT = process.env.PORT || 3000;
9+
const dbPath = path.join(__dirname, "promos.db");
10+
const db = new sqlite3.Database(dbPath);
11+
12+
app.use(cors());
13+
app.use(express.json());
14+
app.use(express.static(path.join(__dirname, "..", "frontend")));
15+
16+
db.serialize(() => {
17+
db.run("CREATE TABLE IF NOT EXISTS promos (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, url TEXT, tags TEXT)");
18+
db.run("CREATE TABLE IF NOT EXISTS clicks (promo_id INTEGER PRIMARY KEY, count INTEGER)");
19+
});
20+
21+
app.post("/api/promos", (req, res) => {
22+
const { title, url, tags } = req.body;
23+
db.run("INSERT INTO promos (title, url, tags) VALUES (?, ?, ?)", [title, url, JSON.stringify(tags)], function(err) {
24+
if (err) return res.status(500).json({ error: "Insert failed" });
25+
res.json({ id: this.lastID, title, url, tags });
26+
});
27+
});
28+
29+
app.get("/api/promos", (req, res) => {
30+
db.all("SELECT * FROM promos", [], (err, rows) => {
31+
if (err) return res.status(500).json({ error: "Read failed" });
32+
const promos = rows.map(p => ({ ...p, tags: JSON.parse(p.tags || "[]") }));
33+
res.json(promos);
34+
});
35+
});
36+
37+
app.post("/api/click", (req, res) => {
38+
const { id } = req.body;
39+
db.run("INSERT INTO clicks (promo_id, count) VALUES (?, 1) ON CONFLICT(promo_id) DO UPDATE SET count = count + 1", [id], err => {
40+
if (err) return res.status(500).json({ error: "Click error" });
41+
res.json({ success: true });
42+
});
43+
});
44+
45+
app.get("/api/feed", (req, res) => {
46+
db.all("SELECT * FROM promos", [], (err, rows) => {
47+
if (err) return res.status(500).json({ error: "Feed error" });
48+
const promos = rows.map(p => ({ ...p, tags: JSON.parse(p.tags || "[]") }));
49+
res.json(promos);
50+
});
51+
});
52+
53+
app.listen(PORT, () => console.log(`Backend up on ${PORT}`));

fly.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
app = "gamblecodez-app"
2+
3+
[build]
4+
builder = "paketobuildpacks/builder:base"
5+
6+
[env]
7+
NODE_ENV = "production"
8+
9+
[experimental]
10+
auto_rollback = true

frontend/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Frontend placeholder

frontend/index.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@tailwind base;
2+
@tailwind components;
3+
@tailwind utilities;

frontend/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<!DOCTYPE html><html><head><title>Promos</title></head><body><div id='root'></div><script type='module' src='/src/App.jsx'></script></body></html>

frontend/src/App.jsx

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
import React, { useEffect, useState } from "react";
3+
const TAGS = ["KYC", "VPN", "Faucet", "Survey", "US", "Non-US"];
4+
export default function App() {
5+
const [promos, setPromos] = useState([]);
6+
const [form, setForm] = useState({ title: "", url: "", tags: [] });
7+
8+
const loadPromos = async () => {
9+
const res = await fetch("/api/promos");
10+
const data = await res.json();
11+
setPromos(data);
12+
};
13+
14+
const submitPromo = async () => {
15+
await fetch("/api/promos", {
16+
method: "POST",
17+
headers: { "Content-Type": "application/json" },
18+
body: JSON.stringify(form),
19+
});
20+
setForm({ title: "", url: "", tags: [] });
21+
loadPromos();
22+
};
23+
24+
useEffect(() => { loadPromos(); }, []);
25+
26+
return (
27+
<div className="max-w-3xl mx-auto p-4">
28+
<h1 className="text-2xl font-bold">🎯 GambleCodez Promos</h1>
29+
<input className="border p-2 w-full mb-2" placeholder="Promo Title" value={form.title}
30+
onChange={(e) => setForm({ ...form, title: e.target.value })} />
31+
<input className="border p-2 w-full mb-2" placeholder="Promo Link" value={form.url}
32+
onChange={(e) => setForm({ ...form, url: e.target.value })} />
33+
<div className="flex flex-wrap gap-2 mb-2">
34+
{TAGS.map(tag => (
35+
<label key={tag}><input type="checkbox" checked={form.tags.includes(tag)}
36+
onChange={() => {
37+
const tags = form.tags.includes(tag) ? form.tags.filter(t => t !== tag) : [...form.tags, tag];
38+
setForm({ ...form, tags });
39+
}} /> {tag}
40+
</label>
41+
))}
42+
</div>
43+
<button className="bg-blue-600 text-white px-4 py-2 rounded" onClick={submitPromo}>Add Promo</button>
44+
<div className="mt-6 grid gap-4">
45+
{promos.map((p, i) => (
46+
<div key={i} className="border p-4 bg-white rounded shadow">
47+
<a href={p.url} target="_blank" className="text-blue-600 font-semibold">{p.title}</a>
48+
<div className="mt-2 flex flex-wrap gap-2">{(p.tags || []).map((tag, i) => (
49+
<span key={i} className="bg-gray-200 text-xs px-2 py-1 rounded">{tag}</span>
50+
))}</div>
51+
</div>
52+
))}
53+
</div>
54+
</div>
55+
);
56+
}

github/workflows/deploy.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Deploy to Fly.io
2+
3+
on:
4+
push:
5+
branches: [main]
6+
7+
jobs:
8+
deploy:
9+
name: Deploy app
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v3
13+
14+
- name: Set up Node
15+
uses: actions/setup-node@v3
16+
with:
17+
node-version: 18
18+
19+
- name: Install backend dependencies
20+
run: cd backend && npm install
21+
22+
- name: Install frontend dependencies
23+
run: cd frontend && npm install && npm run build
24+
25+
- name: Install Fly CLI
26+
uses: superfly/flyctl-actions/setup-flyctl@master
27+
28+
- name: Deploy app
29+
run: flyctl deploy --remote-only
30+
env:
31+
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}

0 commit comments

Comments
 (0)