Initial commit - PezkuwiChain Telegram MiniApp

This commit is contained in:
2026-02-05 10:48:14 +03:00
commit ddd28705c1
105 changed files with 29195 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env node
/**
* Auto-increment version on each build
* Usage: node scripts/bump-version.js [major|minor|patch]
* Default: patch
*/
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const packagePath = path.resolve(__dirname, '../package.json');
// Read package.json
const pkg = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
// Parse current version
const [major, minor, patch] = pkg.version.split('.').map(Number);
// Determine bump type from argument
const bumpType = process.argv[2] || 'patch';
let newVersion;
switch (bumpType) {
case 'major':
newVersion = `${major + 1}.0.0`;
break;
case 'minor':
newVersion = `${major}.${minor + 1}.0`;
break;
case 'patch':
default:
newVersion = `${major}.${minor}.${patch + 1}`;
break;
}
// Add build timestamp for cache busting
const buildTime = new Date().toISOString();
// Update package.json
pkg.version = newVersion;
fs.writeFileSync(packagePath, JSON.stringify(pkg, null, 2) + '\n');
// Write version info to a separate file for the app
const versionInfo = {
version: newVersion,
buildTime,
buildNumber: Date.now(),
};
const versionFilePath = path.resolve(__dirname, '../src/version.json');
fs.writeFileSync(versionFilePath, JSON.stringify(versionInfo, null, 2) + '\n');
console.log(`✓ Version bumped: ${major}.${minor}.${patch}${newVersion}`);
console.log(`✓ Build time: ${buildTime}`);
+141
View File
@@ -0,0 +1,141 @@
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.SUPABASE_URL || process.env.VITE_SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_KEY;
if (!supabaseUrl || !supabaseKey) {
console.error('Missing: SUPABASE_URL and SUPABASE_SERVICE_KEY required');
process.exit(1);
}
const supabase = createClient(supabaseUrl, supabaseKey);
async function createForumTables() {
console.log('Creating forum tables in DKSapp Supabase...\n');
// Create tables using raw SQL via Supabase's pg_catalog or rpc
// Since we can't run raw SQL directly, let's check if tables exist and create data
// First, let's try to query the tables to see if they exist
console.log('Checking existing tables...');
const { data: categories, error: catError } = await supabase
.from('forum_categories')
.select('*')
.limit(1);
if (catError && catError.message.includes('does not exist')) {
console.log('\n❌ Forum tables do not exist in this Supabase project.');
console.log('\nYou need to create them in Supabase SQL Editor:');
console.log('Go to your Supabase dashboard SQL editor\n');
console.log('Run this SQL:\n');
console.log(`
-- Forum Categories Table
CREATE TABLE IF NOT EXISTS forum_categories (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT UNIQUE NOT NULL,
description TEXT,
icon TEXT DEFAULT '💬',
color TEXT DEFAULT '#3B82F6',
is_active BOOLEAN DEFAULT true,
display_order INTEGER DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Forum Discussions Table
CREATE TABLE IF NOT EXISTS forum_discussions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
category_id UUID REFERENCES forum_categories(id),
proposal_id TEXT,
title TEXT NOT NULL,
content TEXT NOT NULL,
image_url TEXT,
author_id TEXT NOT NULL,
author_name TEXT NOT NULL,
author_address TEXT,
is_pinned BOOLEAN DEFAULT false,
is_locked BOOLEAN DEFAULT false,
views_count INTEGER DEFAULT 0,
replies_count INTEGER DEFAULT 0,
tags TEXT[] DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
last_activity_at TIMESTAMPTZ DEFAULT NOW()
);
-- Forum Replies Table
CREATE TABLE IF NOT EXISTS forum_replies (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
discussion_id UUID REFERENCES forum_discussions(id) ON DELETE CASCADE,
parent_reply_id UUID REFERENCES forum_replies(id),
content TEXT NOT NULL,
author_id TEXT NOT NULL,
author_name TEXT NOT NULL,
author_address TEXT,
is_edited BOOLEAN DEFAULT false,
edited_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Forum Reactions Table
CREATE TABLE IF NOT EXISTS forum_reactions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
discussion_id UUID REFERENCES forum_discussions(id) ON DELETE CASCADE,
reply_id UUID REFERENCES forum_replies(id) ON DELETE CASCADE,
user_id TEXT NOT NULL,
reaction_type TEXT NOT NULL CHECK (reaction_type IN ('upvote', 'downvote')),
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(discussion_id, user_id),
UNIQUE(reply_id, user_id)
);
-- Admin Announcements Table
CREATE TABLE IF NOT EXISTS admin_announcements (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
content TEXT NOT NULL,
type TEXT DEFAULT 'info' CHECK (type IN ('info', 'warning', 'success', 'critical')),
priority INTEGER DEFAULT 0,
is_active BOOLEAN DEFAULT true,
expires_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Enable RLS
ALTER TABLE forum_categories ENABLE ROW LEVEL SECURITY;
ALTER TABLE forum_discussions ENABLE ROW LEVEL SECURITY;
ALTER TABLE forum_replies ENABLE ROW LEVEL SECURITY;
ALTER TABLE forum_reactions ENABLE ROW LEVEL SECURITY;
ALTER TABLE admin_announcements ENABLE ROW LEVEL SECURITY;
-- RLS Policies (allow all for now - can be restricted later)
CREATE POLICY "Allow all on forum_categories" ON forum_categories FOR ALL USING (true) WITH CHECK (true);
CREATE POLICY "Allow all on forum_discussions" ON forum_discussions FOR ALL USING (true) WITH CHECK (true);
CREATE POLICY "Allow all on forum_replies" ON forum_replies FOR ALL USING (true) WITH CHECK (true);
CREATE POLICY "Allow all on forum_reactions" ON forum_reactions FOR ALL USING (true) WITH CHECK (true);
CREATE POLICY "Allow all on admin_announcements" ON admin_announcements FOR ALL USING (true) WITH CHECK (true);
-- Create indexes
CREATE INDEX IF NOT EXISTS idx_discussions_category ON forum_discussions(category_id);
CREATE INDEX IF NOT EXISTS idx_discussions_pinned ON forum_discussions(is_pinned);
CREATE INDEX IF NOT EXISTS idx_discussions_activity ON forum_discussions(last_activity_at DESC);
CREATE INDEX IF NOT EXISTS idx_replies_discussion ON forum_replies(discussion_id);
CREATE INDEX IF NOT EXISTS idx_reactions_discussion ON forum_reactions(discussion_id);
CREATE INDEX IF NOT EXISTS idx_reactions_reply ON forum_reactions(reply_id);
`);
return false;
} else if (catError) {
console.error('Error checking tables:', catError.message);
return false;
} else {
console.log('✅ Forum tables exist!');
console.log(' Categories found:', categories?.length || 0);
return true;
}
}
createForumTables().then(exists => {
if (exists) {
console.log('\nTables ready. Now run seed-admin-post.mjs');
}
}).catch(console.error);
+58
View File
@@ -0,0 +1,58 @@
#!/bin/bash
#
# P2P Fiat Trading E2E Test Runner
#
# This script runs end-to-end tests for the P2P trading system.
# E2E tests require:
# 1. SUPABASE_SERVICE_ROLE_KEY environment variable
# 2. Network access to Supabase
#
# Usage:
# ./scripts/run-e2e-tests.sh
#
# Or with service key:
# SUPABASE_SERVICE_ROLE_KEY="your-key" ./scripts/run-e2e-tests.sh
#
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
echo -e "${YELLOW}========================================${NC}"
echo -e "${YELLOW}P2P Fiat Trading E2E Test Suite${NC}"
echo -e "${YELLOW}========================================${NC}"
echo ""
# Check for service role key
if [ -z "$SUPABASE_SERVICE_ROLE_KEY" ]; then
echo -e "${RED}ERROR: SUPABASE_SERVICE_ROLE_KEY is not set${NC}"
echo ""
echo "E2E tests require the Supabase service role key."
echo "Get it from: https://supabase.com/dashboard/project/vbhftvdayqfmcgmzdxfv/settings/api"
echo ""
echo "Run with:"
echo " export SUPABASE_SERVICE_ROLE_KEY=\"your-key-here\""
echo " ./scripts/run-e2e-tests.sh"
echo ""
echo "Or:"
echo " SUPABASE_SERVICE_ROLE_KEY=\"your-key\" ./scripts/run-e2e-tests.sh"
echo ""
echo -e "${YELLOW}Running unit tests only...${NC}"
npm run test:run -- --grep "P2P Fiat Trading E2E" --invert
exit 0
fi
echo -e "${GREEN}Service role key found. Running full E2E test suite...${NC}"
echo ""
# Run all P2P tests including E2E
npm run test:run -- --grep "P2P"
echo ""
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN}E2E Tests Complete${NC}"
echo -e "${GREEN}========================================${NC}"
+152
View File
@@ -0,0 +1,152 @@
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.SUPABASE_URL || process.env.VITE_SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_KEY || process.env.VITE_SUPABASE_ANON_KEY;
if (!supabaseUrl || !supabaseKey) {
console.error('Missing environment variables: SUPABASE_URL and SUPABASE_SERVICE_KEY required');
process.exit(1);
}
const supabase = createClient(supabaseUrl, supabaseKey);
async function seedAdminPost() {
console.log('Seeding admin post...\n');
// 1. Check/Create category
console.log('1. Checking for Giştî category...');
let { data: existingCategory } = await supabase
.from('forum_categories')
.select('id')
.eq('name', 'Giştî')
.single();
let categoryId;
if (existingCategory) {
categoryId = existingCategory.id;
console.log(' Category exists:', categoryId);
} else {
console.log(' Creating category...');
const { data: newCategory, error: catError } = await supabase
.from('forum_categories')
.insert({
name: 'Giştî',
description: 'Mijarên giştî û nîqaşên civakî',
icon: '💬',
color: '#3B82F6',
is_active: true,
display_order: 1
})
.select()
.single();
if (catError) {
console.error(' Error creating category:', catError.message);
return;
}
categoryId = newCategory.id;
console.log(' Category created:', categoryId);
}
// 2. Check if welcome post exists
console.log('\n2. Checking for existing welcome post...');
const { data: existingPost } = await supabase
.from('forum_discussions')
.select('id')
.eq('author_id', 'admin')
.eq('is_pinned', true)
.single();
if (existingPost) {
console.log(' Welcome post already exists:', existingPost.id);
} else {
console.log(' Creating welcome post...');
const postContent = `Silav û rêz ji hemû welatiyên Kurd ên li seranserê cîhanê! 🌍
Pezkuwichain ne tenê blockchaineke e - ew xewna me ye ku Kurdên li her çar perçeyên Kurdistanê û li diasporayê bi hev re bigihîjin.
🔗 **Çima Pezkuwichain?**
Di vê serdema dîjîtal de, em Kurd hewce ne ku platformeke xwe hebe ku:
- Bi azadî bi hev re biaxivin
- Aboriya xwe bi hev re ava bikin
- Nasname û çanda xwe biparêzin
- Ji hêza teknolojiya blockchain ve ewlekariya xwe misoger bikin
🌐 **Armanca Me**
Em dixwazin platformeke ava bikin ku hemû Kurdên cîhanê dikarin:
- Bi hev re pêwendî daynin (connect)
- Projeyên hevbeş bi rê ve bibin
- Bi ziman û çanda xwe bi hev re têkildar bin
- Di aboriya dîjîtal de cîhê xwe bigirin
💪 **Hêza Civakê**
Blockchain ne tenê teknolojî ye - ew şanseke nû ye ji bo gelên wekî me ku di dîrokê de nekarîbûn dewleta xwe ava bikin. Niha em dikarin "Dewleta Dîjîtal"a xwe ava bikin!
Ev platform diyarîyeke ji me ye ji bo hemû Kurdên cîhanê. Bi hev re em dikarin!
#BiHevRe #Pezkuwichain #Blockchain #Kurd #DewletaDîjîtal`;
const { data: newPost, error: postError } = await supabase
.from('forum_discussions')
.insert({
category_id: categoryId,
title: 'Bi xêr hatî Pezkuwichain! 🎉 Dewleta Dîjîtal a Kurd',
content: postContent,
image_url: '/tokens/DKState.png',
author_id: 'admin',
author_name: 'Pezkuwichain Admin',
is_pinned: true,
is_locked: false,
views_count: 0,
replies_count: 0,
tags: ['BiHevRe', 'Pezkuwichain', 'Blockchain', 'Kurd', 'Civak'],
last_activity_at: new Date().toISOString()
})
.select()
.single();
if (postError) {
console.error(' Error creating post:', postError.message);
} else {
console.log(' Welcome post created:', newPost.id);
}
}
// 3. Check/Create announcement
console.log('\n3. Checking for welcome announcement...');
const { data: existingAnnouncement } = await supabase
.from('admin_announcements')
.select('id')
.eq('title', 'Bi xêr hatî Forum!')
.single();
if (existingAnnouncement) {
console.log(' Announcement already exists:', existingAnnouncement.id);
} else {
console.log(' Creating announcement...');
const { data: newAnnouncement, error: annError } = await supabase
.from('admin_announcements')
.insert({
title: 'Bi xêr hatî Forum!',
content: 'Ev foruma fermî ya Pezkuwichain e. Li vir tu dikarî mijarên nû vekî, bersivan bidî û bi civakê re têkiliyê ragirî!',
type: 'success',
priority: 100,
is_active: true
})
.select()
.single();
if (annError) {
console.error(' Error creating announcement:', annError.message);
} else {
console.log(' Announcement created:', newAnnouncement.id);
}
}
console.log('\n✅ Seeding complete!');
}
seedAdminPost().catch(console.error);
+113
View File
@@ -0,0 +1,113 @@
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.SUPABASE_URL || process.env.VITE_SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_KEY;
if (!supabaseUrl || !supabaseKey) {
console.error('Missing: SUPABASE_URL and SUPABASE_SERVICE_KEY required');
process.exit(1);
}
const supabase = createClient(supabaseUrl, supabaseKey);
async function seedRagihandin() {
console.log('Creating Ragihandin (Announcements) section...\n');
// 1. Create or get Ragihandin category
console.log('1. Checking for Ragihandin category...');
let { data: existingCategory } = await supabase
.from('forum_categories')
.select('id')
.eq('name', 'Ragihandin')
.single();
let categoryId;
if (existingCategory) {
categoryId = existingCategory.id;
console.log(' Category exists:', categoryId);
} else {
console.log(' Creating Ragihandin category...');
const { data: newCategory, error: catError } = await supabase
.from('forum_categories')
.insert({
name: 'Ragihandin',
description: 'Daxuyaniyên fermî yên Pezkuwichain',
icon: '📢',
color: '#EF4444',
is_active: true,
display_order: 0 // First in the list
})
.select()
.single();
if (catError) {
console.error(' Error creating category:', catError.message);
return;
}
categoryId = newCategory.id;
console.log(' Category created:', categoryId);
}
// 2. Create the pinned announcement post
console.log('\n2. Creating pinned announcement post...');
const { data: existingPost } = await supabase
.from('forum_discussions')
.select('id')
.eq('category_id', categoryId)
.eq('author_id', 'admin')
.eq('is_pinned', true)
.single();
if (existingPost) {
console.log(' Pinned announcement already exists:', existingPost.id);
} else {
const announcementContent = `Hûn bi xêr hatin rûpela daxuyaniyên fermî yên Pezkuwichain! 🎉
📢 **Ev Çi Ye?**
Ev rûpela yekem û yekane ya daxuyaniyên fermî yên Pezkuwichain e. Hemû nûçe, update, û daxuyaniyên girîng ên projeyê dê li vir werin weşandin.
✅ **Li Vir Hûn Dikarin:**
- Nûçeyên herî dawî yên Pezkuwichain bişopînin
- Updateyên teknîkî û pêşkeftinên nû bibînin
- Daxuyaniyên fermî yên tîmê bixwînin
- Pêşerojê û roadmapê bişopînin
⚠️ **Girîng:**
Tenê admin dikare li vê beşê post bike. Ev garantî dike ku hemû agahdarî rast û fermî ne.
🔔 Bi me re bimînin û nûçeyên herî dawî werin!
#Pezkuwichain #Ragihandin #Fermî #Daxuyanî`;
const { data: newPost, error: postError } = await supabase
.from('forum_discussions')
.insert({
category_id: categoryId,
title: '📢 Bi Xêr Hatî Rûpela Daxuyaniyên Fermî yên Pezkuwichain!',
content: announcementContent,
image_url: '/tokens/pezkuwichain_header.png',
author_id: 'admin',
author_name: 'Pezkuwichain Admin',
is_pinned: true,
is_locked: false,
views_count: 0,
replies_count: 0,
tags: ['Pezkuwichain', 'Ragihandin', 'Fermî', 'Daxuyanî'],
last_activity_at: new Date().toISOString()
})
.select()
.single();
if (postError) {
console.error(' Error creating post:', postError.message);
} else {
console.log(' Announcement post created:', newPost.id);
}
}
console.log('\n✅ Ragihandin section ready!');
}
seedRagihandin().catch(console.error);
+122
View File
@@ -0,0 +1,122 @@
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.SUPABASE_URL || process.env.VITE_SUPABASE_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_KEY;
if (!supabaseUrl || !supabaseKey) {
console.error('Missing: SUPABASE_URL and SUPABASE_SERVICE_KEY required');
process.exit(1);
}
const supabase = createClient(supabaseUrl, supabaseKey);
async function seedAnnouncements() {
console.log('Setting up Ragihandin (tg_announcements)...\n');
// 1. Check/create admin user
console.log('1. Checking for admin user...');
let { data: adminUser } = await supabase
.from('tg_users')
.select('*')
.eq('is_admin', true)
.single();
if (!adminUser) {
console.log(' No admin user found, checking all users...');
const { data: allUsers } = await supabase
.from('tg_users')
.select('*')
.limit(5);
console.log(' Found', allUsers?.length || 0, 'users');
if (allUsers && allUsers.length > 0) {
console.log(' Users:', allUsers.map(u => `${u.first_name} (${u.id})`).join(', '));
// Use the first user as admin for now
adminUser = allUsers[0];
console.log(' Using first user as admin:', adminUser.first_name);
} else {
// Create a system admin user
console.log(' Creating system admin user...');
const { data: newAdmin, error: adminError } = await supabase
.from('tg_users')
.insert({
telegram_id: 0,
username: 'pezkuwichain_admin',
first_name: 'Pezkuwichain',
last_name: 'Admin',
is_admin: true
})
.select()
.single();
if (adminError) {
console.error(' Error creating admin:', adminError.message);
return false;
}
adminUser = newAdmin;
console.log(' Admin created:', adminUser.id);
}
} else {
console.log(' Admin found:', adminUser.first_name, adminUser.id);
}
// 2. Check existing announcements
console.log('\n2. Checking existing announcements...');
const { data: existing } = await supabase
.from('tg_announcements')
.select('*')
.limit(5);
console.log(' Found', existing?.length || 0, 'announcements');
const ourAnnouncement = existing?.find(a =>
a.title?.includes('Bi Xêr Hatî') || a.title?.includes('Daxuyaniyên Fermî')
);
if (ourAnnouncement) {
console.log('\n ✅ Admin announcement already exists:', ourAnnouncement.id);
return true;
}
// 3. Create the admin announcement
console.log('\n3. Creating admin announcement...');
const announcementContent = `Hûn bi xêr hatin rûpela daxuyaniyên fermî yên Pezkuwichain!
📢 Ev rûpela yekem û yekane ya daxuyaniyên fermî yên Pezkuwichain e.
✅ Li vir hûn dikarin:
• Nûçeyên herî dawî yên Pezkuwichain bişopînin
• Updateyên teknîkî û pêşkeftinên nû bibînin
• Daxuyaniyên fermî yên tîmê bixwînin
• Roadmap û pêşerojê bişopînin
⚠️ Tenê admin dikare li vir post bike - hemû agahdarî rast û fermî ne.
🔔 Bi me re bimînin!`;
const { data: newAnn, error: annError } = await supabase
.from('tg_announcements')
.insert({
title: '📢 Bi Xêr Hatî Rûpela Daxuyaniyên Fermî!',
content: announcementContent,
image_url: '/tokens/pezkuwichain_header.png',
link_url: 'https://pezkuwichain.io',
author_id: adminUser.id,
is_published: true
})
.select()
.single();
if (annError) {
console.error(' Error creating announcement:', annError.message);
return false;
}
console.log(' ✅ Announcement created:', newAnn.id);
console.log('\n✅ Ragihandin setup complete!');
return true;
}
seedAnnouncements().catch(console.error);
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
# PezkuwiChain Telegram Bot Webhook Setup
# Run this script from a machine that can access Telegram API
BOT_TOKEN="8548408481:AAEsoyiVBllk_x0T3Jelj8N8VrUiuc9jXQw"
WEBHOOK_URL="https://vbhftvdayqfmcgmzdxfv.supabase.co/functions/v1/telegram-bot"
echo "Setting up Telegram webhook..."
echo "Bot Token: ${BOT_TOKEN:0:10}..."
echo "Webhook URL: $WEBHOOK_URL"
echo ""
# Delete any existing webhook
echo "Removing existing webhook..."
curl -s "https://api.telegram.org/bot$BOT_TOKEN/deleteWebhook" | jq .
# Set new webhook
echo ""
echo "Setting new webhook..."
curl -s "https://api.telegram.org/bot$BOT_TOKEN/setWebhook" \
-d "url=$WEBHOOK_URL" \
-d "allowed_updates=[\"message\",\"callback_query\"]" | jq .
# Verify webhook
echo ""
echo "Verifying webhook..."
curl -s "https://api.telegram.org/bot$BOT_TOKEN/getWebhookInfo" | jq .
echo ""
echo "Done! Test the bot by sending /start to @pezkuwichain_bot"