swadhinbiswas feat: optimize Docker CI/CD builds, refine site-wide UI aesthetic, and remediate secrets · 8/19/2026
c3e5c18
180 lines · 7.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
---
import BaseLayout from "../../layouts/BaseLayout.astro";
import { Header } from "../../components/layout/Header";
import { Card, CardContent } from "../../components/ui/card";
import { Button } from "../../components/ui/button";
import { Badge } from "../../components/ui/badge";
import { 
  Globe, 
  Plus, 
  Server, 
  Activity, 
  Users, 
  BookOpen, 
  ExternalLink, 
  ArrowRight,
  ShieldCheck,
  CheckCircle2,
  AlertCircle
} from "lucide-react";
import { verifyToken } from "../../lib/auth";
import { getDb } from "../../lib/db";
import { desc } from "drizzle-orm";
import { instances } from "../../lib/db/schema";

const token = Astro.cookies.get("community_session")?.value;
const user = token ? await verifyToken(token) : null;

const db = getDb() as any;
const allInstances = await db.query.instances.findMany({
  orderBy: [desc(instances.repoCount)]
}).catch(() => []);
---
<BaseLayout title="Federated Instances — OpenCodeHub Community">
  <Header client:load user={user} />
  
  <main class="container px-4 sm:px-8 py-8 sm:py-12 space-y-8">
    
    <!-- Top Header -->
    <div class="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pb-6 border-b border-border/50">
      <div class="space-y-1">
        <h1 class="text-3xl font-extrabold tracking-tight text-foreground flex items-center gap-2.5">
          <Server className="h-7 w-7 text-primary" />
          Federated Nodes
        </h1>
        <p class="text-sm text-muted-foreground">
          Every registered OpenCodeHub instance across the decentralized ecosystem.
        </p>
      </div>

      <a href="/instances/submit">
        <Button className="gap-2 bg-primary hover:bg-primary/90 shadow-md shadow-primary/20 text-xs font-semibold">
          <Plus className="h-4 w-4" />
          Submit Node
        </Button>
      </a>
    </div>

    <!-- Instances Grid -->
    {allInstances.length === 0 ? (
      <div class="rounded-3xl border border-dashed border-border/80 bg-card/40 p-12 text-center space-y-4 max-w-xl mx-auto">
        <div class="h-14 w-14 rounded-2xl bg-primary/10 text-primary flex items-center justify-center mx-auto shadow-inner">
          <Globe className="h-7 w-7" />
        </div>
        <div class="space-y-1">
          <h3 class="font-bold text-lg text-foreground">No instances registered yet</h3>
          <p class="text-xs text-muted-foreground max-w-md mx-auto leading-relaxed">
            Be the first pioneer! Submit your self-hosted OpenCodeHub URL. We will probe <code>/api/instance</code> and start aggregating your public repos.
          </p>
        </div>
        <div class="pt-2">
          <a href="/instances/submit">
            <Button size="sm" className="gap-1.5 text-xs">
              <Plus className="h-3.5 w-3.5" />
              Submit Your Instance
            </Button>
          </a>
        </div>
      </div>
    ) : (
      <div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
        {allInstances.map((instance: any) => {
          const isOnline = instance.status === "online";
          let capabilitiesList: string[] = [];
          try {
            if (instance.capabilities) {
              capabilitiesList = typeof instance.capabilities === "string" ? JSON.parse(instance.capabilities) : instance.capabilities;
            }
          } catch {}

          return (
            <div class="rounded-2xl border border-border/60 bg-card/60 backdrop-blur-sm p-6 hover:border-primary/40 hover:bg-card/90 transition-all shadow-sm flex flex-col justify-between gap-6 group">
              <div class="space-y-4">
                <div class="flex items-start justify-between gap-3">
                  <div class="flex items-center gap-3">
                    <div class="h-10 w-10 rounded-xl bg-primary/10 border border-primary/20 flex items-center justify-center text-primary shrink-0">
                      <Server className="h-5 w-5" />
                    </div>
                    <div class="min-w-0">
                      <a href={`/instances/${instance.id}`} class="font-bold text-base text-foreground hover:text-primary transition-colors truncate block">
                        {instance.name}
                      </a>
                      <a href={instance.siteUrl} target="_blank" class="text-xs text-muted-foreground hover:underline truncate block">
                        {instance.siteUrl}
                      </a>
                    </div>
                  </div>

                  <!-- Status Pulse Pill -->
                  <div class={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full text-[11px] font-semibold border ${
                    isOnline 
                      ? "bg-emerald-500/10 text-emerald-400 border-emerald-500/20" 
                      : "bg-destructive/10 text-destructive border-destructive/20"
                  }`}>
                    <span class={`h-1.5 w-1.5 rounded-full ${isOnline ? "bg-emerald-400 animate-pulse" : "bg-destructive"}`}></span>
                    <span class="capitalize">{instance.status || "pending"}</span>
                  </div>
                </div>

                <!-- Capabilities Badges -->
                <div class="flex flex-wrap gap-1.5 pt-1">
                  {instance.version && (
                    <Badge variant="outline" className="text-[10px] font-mono">
                      v{instance.version}
                    </Badge>
                  )}
                  {capabilitiesList.slice(0, 3).map((cap: string) => (
                    <Badge variant="secondary" className="text-[10px] uppercase font-semibold tracking-wider">
                      {cap}
                    </Badge>
                  ))}
                </div>
              </div>

              <!-- Bottom Stats & Action Bar -->
              <div class="pt-4 border-t border-border/50 flex flex-col gap-3">
                <div class="grid grid-cols-2 gap-2 text-xs text-muted-foreground">
                  <div class="flex items-center gap-1.5">
                    <BookOpen className="h-3.5 w-3.5 text-primary" />
                    <span class="font-medium text-foreground">{instance.repoCount || 0}</span> repos
                  </div>
                  <div class="flex items-center gap-1.5">
                    <Activity className="h-3.5 w-3.5 text-indigo-400" />
                    <span>{instance.lastSyncAt ? new Date(instance.lastSyncAt).toLocaleDateString() : 'Pending'}</span>
                  </div>
                </div>

                <div class="grid grid-cols-2 gap-2 pt-1">
                  <a href={`/instances/${instance.id}`} class="w-full">
                    <Button size="sm" variant="outline" className="w-full text-xs gap-1 hover:bg-primary hover:text-primary-foreground hover:border-primary">
                      Explore Node
                      <ArrowRight className="h-3 w-3" />
                    </Button>
                  </a>
                  <a href={instance.siteUrl} target="_blank" rel="noreferrer" class="w-full">
                    <Button size="sm" variant="ghost" className="w-full text-xs gap-1 text-muted-foreground hover:text-foreground">
                      <ExternalLink className="h-3 w-3" />
                      Visit Site
                    </Button>
                  </a>
                </div>
              </div>
            </div>
          );
        })}
      </div>
    )}

    <!-- Privacy Note Box -->
    <div class="rounded-2xl border border-border/60 bg-muted/20 p-5 text-xs text-muted-foreground flex items-start gap-3">
      <ShieldCheck className="h-5 w-5 text-primary shrink-0 mt-0.5" />
      <div class="space-y-1">
        <span class="font-semibold text-foreground">Zero-Access Privacy Guarantee:</span>
        <p class="leading-relaxed">
          Only public repositories (<code>visibility=public</code>) and public author identities are aggregated. Internal codebases, private keys, and user tokens are completely untouched.
        </p>
      </div>
    </div>
  </main>
</BaseLayout>