1import { GitHubCommit } from '../shared/types';
2
3// GitHub API client
4export class GitHubClient {
5 private baseUrl = 'https://api.github.com';
6 private token: string;
7
29 if (!response.ok) {
30 const error = await response.text();
31 throw new Error(`GitHub API error (${response.status}): ${error}`);
32 }
33
29## Project Structure
30
31- `backend/` - API endpoints and GitHub integration
32- `frontend/` - User interface for the app
33- `shared/` - Shared types and utilities
36
37- TypeScript
38- GitHub REST API
39- Hono (backend framework)
40- React (frontend)
24app.use("/*", cors());
25
26// API routes
27app.route("/api/auth", auth);
28app.route("/api/tickets", tickets);
29app.route("/api/verification", verification);
30
31// Serve static files
40 // Add initial data to avoid extra round-trips
41 const initialData = {
42 apiUrl: "/api"
43 };
44
21});
22
23// API Routes
24app.get("/api/todos", async (c) => {
25 const todos = await getTodos();
26 return c.json(todos);
27});
28
29app.post("/api/todos", async (c) => {
30 const data = await c.req.json();
31 const todo = await addTodo(data.text);
33});
34
35app.put("/api/todos/:id", async (c) => {
36 const id = parseInt(c.req.param("id"));
37 const data = await c.req.json();
40});
41
42app.delete("/api/todos/:id", async (c) => {
43 const id = parseInt(c.req.param("id"));
44 await deleteTodo(id);
3import { UserProfile } from "../../shared/types.ts";
4
5// Get API URL from initial data
6const apiUrl = (window as any).__INITIAL_DATA__?.apiUrl || "/api";
7
8interface AuthProps {
48
49 // Send request
50 const response = await fetch(`${apiUrl}/auth/${endpoint}`, {
51 method: "POST",
52 headers: {
19
20// Get initial data from the server
21const initialData = (window as any).__INITIAL_DATA__ || { apiUrl: "/api" };
22
23// App state types
102 const fetchTickets = async (token: string) => {
103 try {
104 const response = await fetch(`${initialData.apiUrl}/tickets`, {
105 headers: {
106 "Authorization": `Bearer ${token}`
141 const handleTicketPurchase = async (ticketType: string) => {
142 try {
143 const response = await fetch(`${initialData.apiUrl}/tickets/purchase`, {
144 method: "POST",
145 headers: {
180 const handleVerifyTicket = async (code: string) => {
181 try {
182 const response = await fetch(`${initialData.apiUrl}/verification/verify`, {
183 method: "POST",
184 headers: {
2import { jwtMiddleware } from "./auth.ts";
3import { getTicketByVerificationCode, verifyTicket } from "../database/queries.ts";
4import { ApiResponse, VerificationRequest, VerificationResponse } from "../../shared/types.ts";
5
6const verification = new Hono();
12
13 if (!code) {
14 return c.json<ApiResponse<null>>({
15 success: false,
16 error: "Verification code is required"
21
22 if (!ticket) {
23 return c.json<ApiResponse<VerificationResponse>>({
24 success: true,
25 data: {
34 // Check if ticket is expired
35 if (ticket.expirationTime < now) {
36 return c.json<ApiResponse<VerificationResponse>>({
37 success: true,
38 data: {
46 // Check if ticket is already used
47 if (ticket.status !== "active") {
48 return c.json<ApiResponse<VerificationResponse>>({
49 success: true,
50 data: {
59 await verifyTicket(ticket.id);
60
61 return c.json<ApiResponse<VerificationResponse>>({
62 success: true,
63 data: {
72 } catch (error) {
73 console.error("Error verifying ticket:", error);
74 return c.json<ApiResponse<null>>({
75 success: false,
76 error: "Failed to verify ticket"
86 // Check if user is admin
87 if (!isAdmin) {
88 return c.json<ApiResponse<null>>({
89 success: false,
90 error: "Unauthorized"
95
96 if (!code) {
97 return c.json<ApiResponse<null>>({
98 success: false,
99 error: "Verification code is required"
104
105 if (!ticket) {
106 return c.json<ApiResponse<null>>({
107 success: false,
108 error: "Invalid ticket code"
116
117 if (!verified) {
118 return c.json<ApiResponse<null>>({
119 success: false,
120 error: "Failed to mark ticket as used"
122 }
123
124 return c.json<ApiResponse<{ message: string }>>({
125 success: true,
126 data: {
130 } catch (error) {
131 console.error("Error marking ticket as used:", error);
132 return c.json<ApiResponse<null>>({
133 success: false,
134 error: "Failed to mark ticket as used"
21)
22
23// app.get('/api/counter/get', async (c) => c.json(await db.get('counter')))
24// app.get('/api/counter/increment', async (c) => c.json(await db.set('counter', ((await db.get('counter')) || 0) + 1)))
25
26app.get('/neynar-proxy', async (c) => {
3import { createTicket, getUserTickets, updateTicketStatuses } from "../database/queries.ts";
4import { TICKET_TYPES } from "../database/migrations.ts";
5import { ApiResponse, Ticket, TicketType } from "../../shared/types.ts";
6
7const tickets = new Hono();
21 const userTickets = await getUserTickets(userId);
22
23 return c.json<ApiResponse<Ticket[]>>({
24 success: true,
25 data: userTickets
27 } catch (error) {
28 console.error("Error fetching tickets:", error);
29 return c.json<ApiResponse<null>>({
30 success: false,
31 error: "Failed to fetch tickets"
42 // Validate ticket type
43 if (!ticketType || !TICKET_TYPES[ticketType as TicketType]) {
44 return c.json<ApiResponse<null>>({
45 success: false,
46 error: "Invalid ticket type"
51 const ticket = await createTicket(userId, ticketType as TicketType);
52
53 return c.json<ApiResponse<Ticket>>({
54 success: true,
55 data: ticket
57 } catch (error) {
58 console.error("Error purchasing ticket:", error);
59 return c.json<ApiResponse<null>>({
60 success: false,
61 error: "Failed to purchase ticket"
67tickets.get("/types", (c) => {
68 try {
69 return c.json<ApiResponse<typeof TICKET_TYPES>>({
70 success: true,
71 data: TICKET_TYPES
73 } catch (error) {
74 console.error("Error fetching ticket types:", error);
75 return c.json<ApiResponse<null>>({
76 success: false,
77 error: "Failed to fetch ticket types"
2import { jwt } from "https://esm.sh/hono@3.2.7/jwt";
3import { createUser, getUserByUsername } from "../database/queries.ts";
4import { LoginRequest, RegisterRequest, AuthResponse, ApiResponse, UserProfile } from "../../shared/types.ts";
5import { createHash } from "https://deno.land/std@0.177.0/node/crypto.ts";
6
24 // Validate input
25 if (!username || !password) {
26 return c.json<ApiResponse<null>>({
27 success: false,
28 error: "Username and password are required"
33 const user = await getUserByUsername(username);
34 if (!user) {
35 return c.json<ApiResponse<null>>({
36 success: false,
37 error: "Invalid username or password"
42 const passwordHash = hashPassword(password);
43 if (passwordHash !== user.passwordHash) {
44 return c.json<ApiResponse<null>>({
45 success: false,
46 error: "Invalid username or password"
58 };
59
60 return c.json<ApiResponse<AuthResponse>>({
61 success: true,
62 data: {
67 } catch (error) {
68 console.error("Login error:", error);
69 return c.json<ApiResponse<null>>({
70 success: false,
71 error: "An error occurred during login"
82 // Validate input
83 if (!username || !password || !email) {
84 return c.json<ApiResponse<null>>({
85 success: false,
86 error: "Username, password, and email are required"
91 const existingUser = await getUserByUsername(username);
92 if (existingUser) {
93 return c.json<ApiResponse<null>>({
94 success: false,
95 error: "Username already exists"
110 };
111
112 return c.json<ApiResponse<AuthResponse>>({
113 success: true,
114 data: {
119 } catch (error) {
120 console.error("Registration error:", error);
121 return c.json<ApiResponse<null>>({
122 success: false,
123 error: "An error occurred during registration"