llmapi-v2 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +40 -0
- package/Dockerfile +17 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.js +98 -0
- package/dist/config.js.map +1 -0
- package/dist/converter/request.d.ts +6 -0
- package/dist/converter/request.js +184 -0
- package/dist/converter/request.js.map +1 -0
- package/dist/converter/response.d.ts +6 -0
- package/dist/converter/response.js +76 -0
- package/dist/converter/response.js.map +1 -0
- package/dist/converter/stream.d.ts +54 -0
- package/dist/converter/stream.js +318 -0
- package/dist/converter/stream.js.map +1 -0
- package/dist/converter/types.d.ts +239 -0
- package/dist/converter/types.js +6 -0
- package/dist/converter/types.js.map +1 -0
- package/dist/data/posts.d.ts +19 -0
- package/dist/data/posts.js +462 -0
- package/dist/data/posts.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +233 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/api-key-auth.d.ts +6 -0
- package/dist/middleware/api-key-auth.js +76 -0
- package/dist/middleware/api-key-auth.js.map +1 -0
- package/dist/middleware/quota-guard.d.ts +10 -0
- package/dist/middleware/quota-guard.js +27 -0
- package/dist/middleware/quota-guard.js.map +1 -0
- package/dist/middleware/rate-limiter.d.ts +5 -0
- package/dist/middleware/rate-limiter.js +50 -0
- package/dist/middleware/rate-limiter.js.map +1 -0
- package/dist/middleware/request-logger.d.ts +6 -0
- package/dist/middleware/request-logger.js +37 -0
- package/dist/middleware/request-logger.js.map +1 -0
- package/dist/middleware/session-auth.d.ts +19 -0
- package/dist/middleware/session-auth.js +99 -0
- package/dist/middleware/session-auth.js.map +1 -0
- package/dist/providers/aliyun.d.ts +13 -0
- package/dist/providers/aliyun.js +20 -0
- package/dist/providers/aliyun.js.map +1 -0
- package/dist/providers/base-provider.d.ts +36 -0
- package/dist/providers/base-provider.js +133 -0
- package/dist/providers/base-provider.js.map +1 -0
- package/dist/providers/deepseek.d.ts +11 -0
- package/dist/providers/deepseek.js +18 -0
- package/dist/providers/deepseek.js.map +1 -0
- package/dist/providers/registry.d.ts +18 -0
- package/dist/providers/registry.js +98 -0
- package/dist/providers/registry.js.map +1 -0
- package/dist/providers/types.d.ts +17 -0
- package/dist/providers/types.js +3 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/routes/admin.d.ts +1 -0
- package/dist/routes/admin.js +153 -0
- package/dist/routes/admin.js.map +1 -0
- package/dist/routes/auth.d.ts +2 -0
- package/dist/routes/auth.js +318 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/blog.d.ts +1 -0
- package/dist/routes/blog.js +29 -0
- package/dist/routes/blog.js.map +1 -0
- package/dist/routes/dashboard.d.ts +1 -0
- package/dist/routes/dashboard.js +184 -0
- package/dist/routes/dashboard.js.map +1 -0
- package/dist/routes/messages.d.ts +1 -0
- package/dist/routes/messages.js +309 -0
- package/dist/routes/messages.js.map +1 -0
- package/dist/routes/models.d.ts +1 -0
- package/dist/routes/models.js +39 -0
- package/dist/routes/models.js.map +1 -0
- package/dist/routes/payment.d.ts +1 -0
- package/dist/routes/payment.js +150 -0
- package/dist/routes/payment.js.map +1 -0
- package/dist/routes/sitemap.d.ts +1 -0
- package/dist/routes/sitemap.js +38 -0
- package/dist/routes/sitemap.js.map +1 -0
- package/dist/services/alipay.d.ts +27 -0
- package/dist/services/alipay.js +106 -0
- package/dist/services/alipay.js.map +1 -0
- package/dist/services/database.d.ts +4 -0
- package/dist/services/database.js +170 -0
- package/dist/services/database.js.map +1 -0
- package/dist/services/health-checker.d.ts +13 -0
- package/dist/services/health-checker.js +95 -0
- package/dist/services/health-checker.js.map +1 -0
- package/dist/services/mailer.d.ts +3 -0
- package/dist/services/mailer.js +91 -0
- package/dist/services/mailer.js.map +1 -0
- package/dist/services/metrics.d.ts +56 -0
- package/dist/services/metrics.js +94 -0
- package/dist/services/metrics.js.map +1 -0
- package/dist/services/remote-control.d.ts +20 -0
- package/dist/services/remote-control.js +209 -0
- package/dist/services/remote-control.js.map +1 -0
- package/dist/services/remote-ws.d.ts +5 -0
- package/dist/services/remote-ws.js +143 -0
- package/dist/services/remote-ws.js.map +1 -0
- package/dist/services/usage.d.ts +13 -0
- package/dist/services/usage.js +39 -0
- package/dist/services/usage.js.map +1 -0
- package/dist/utils/errors.d.ts +27 -0
- package/dist/utils/errors.js +48 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +14 -0
- package/dist/utils/logger.js.map +1 -0
- package/docker-compose.yml +19 -0
- package/package.json +39 -0
- package/public/robots.txt +8 -0
- package/src/config.ts +140 -0
- package/src/converter/request.ts +207 -0
- package/src/converter/response.ts +85 -0
- package/src/converter/stream.ts +373 -0
- package/src/converter/types.ts +257 -0
- package/src/data/posts.ts +474 -0
- package/src/index.ts +219 -0
- package/src/middleware/api-key-auth.ts +82 -0
- package/src/middleware/quota-guard.ts +28 -0
- package/src/middleware/rate-limiter.ts +61 -0
- package/src/middleware/request-logger.ts +36 -0
- package/src/middleware/session-auth.ts +91 -0
- package/src/providers/aliyun.ts +16 -0
- package/src/providers/base-provider.ts +148 -0
- package/src/providers/deepseek.ts +14 -0
- package/src/providers/registry.ts +111 -0
- package/src/providers/types.ts +26 -0
- package/src/routes/admin.ts +169 -0
- package/src/routes/auth.ts +369 -0
- package/src/routes/blog.ts +28 -0
- package/src/routes/dashboard.ts +208 -0
- package/src/routes/messages.ts +346 -0
- package/src/routes/models.ts +37 -0
- package/src/routes/payment.ts +189 -0
- package/src/routes/sitemap.ts +40 -0
- package/src/services/alipay.ts +116 -0
- package/src/services/database.ts +187 -0
- package/src/services/health-checker.ts +115 -0
- package/src/services/mailer.ts +90 -0
- package/src/services/metrics.ts +104 -0
- package/src/services/remote-control.ts +226 -0
- package/src/services/remote-ws.ts +145 -0
- package/src/services/usage.ts +57 -0
- package/src/types/express.d.ts +46 -0
- package/src/utils/errors.ts +44 -0
- package/src/utils/logger.ts +8 -0
- package/tsconfig.json +17 -0
- package/views/pages/404.ejs +14 -0
- package/views/pages/admin.ejs +307 -0
- package/views/pages/blog-post.ejs +378 -0
- package/views/pages/blog.ejs +148 -0
- package/views/pages/dashboard.ejs +441 -0
- package/views/pages/docs.ejs +807 -0
- package/views/pages/index.ejs +416 -0
- package/views/pages/login.ejs +170 -0
- package/views/pages/orders.ejs +111 -0
- package/views/pages/pricing.ejs +379 -0
- package/views/pages/register.ejs +397 -0
- package/views/pages/remote.ejs +334 -0
- package/views/pages/settings.ejs +373 -0
- package/views/partials/header.ejs +70 -0
- package/views/partials/nav.ejs +140 -0
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
<%- include('../partials/header') %>
|
|
2
|
+
|
|
3
|
+
<!-- JSON-LD Structured Data -->
|
|
4
|
+
<script type="application/ld+json">
|
|
5
|
+
{
|
|
6
|
+
"@context": "https://schema.org",
|
|
7
|
+
"@type": "Article",
|
|
8
|
+
"headline": "<%= post.title %>",
|
|
9
|
+
"description": "<%= post.description %>",
|
|
10
|
+
"author": {
|
|
11
|
+
"@type": "Person",
|
|
12
|
+
"name": "<%= post.author %>"
|
|
13
|
+
},
|
|
14
|
+
"datePublished": "<%= post.date %>",
|
|
15
|
+
"publisher": {
|
|
16
|
+
"@type": "Organization",
|
|
17
|
+
"name": "LLM API",
|
|
18
|
+
"url": "https://llmapi.pro"
|
|
19
|
+
},
|
|
20
|
+
"mainEntityOfPage": {
|
|
21
|
+
"@type": "WebPage",
|
|
22
|
+
"@id": "https://llmapi.pro/blog/<%= post.slug %>"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<!-- marked.js for Markdown rendering -->
|
|
28
|
+
<script src="https://cdn.jsdelivr.net/npm/marked@12/marked.min.js"></script>
|
|
29
|
+
|
|
30
|
+
<%- include('../partials/nav') %>
|
|
31
|
+
|
|
32
|
+
<!-- Breadcrumb -->
|
|
33
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-6">
|
|
34
|
+
<nav class="flex items-center text-sm text-gray-400 space-x-2">
|
|
35
|
+
<a href="/" class="hover:text-claude-orange transition-colors">Home</a>
|
|
36
|
+
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
37
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
|
38
|
+
</svg>
|
|
39
|
+
<a href="/blog" class="hover:text-claude-orange transition-colors">Blog</a>
|
|
40
|
+
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
41
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
|
|
42
|
+
</svg>
|
|
43
|
+
<span class="text-gray-600 truncate max-w-xs"><%= post.title %></span>
|
|
44
|
+
</nav>
|
|
45
|
+
</div>
|
|
46
|
+
|
|
47
|
+
<!-- Post Content -->
|
|
48
|
+
<section class="pb-20 sm:pb-24">
|
|
49
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-8">
|
|
50
|
+
<div class="flex flex-col lg:flex-row gap-10">
|
|
51
|
+
|
|
52
|
+
<!-- Main Content -->
|
|
53
|
+
<article class="lg:w-2/3">
|
|
54
|
+
<!-- Post Header -->
|
|
55
|
+
<header class="mb-8">
|
|
56
|
+
<span class="inline-block px-2.5 py-0.5 text-xs font-medium text-claude-orange bg-claude-orange/10 rounded-full mb-4">
|
|
57
|
+
<%= post.category %>
|
|
58
|
+
</span>
|
|
59
|
+
<h1 class="text-3xl sm:text-4xl font-bold text-claude-dark leading-tight mb-4">
|
|
60
|
+
<%= post.title %>
|
|
61
|
+
</h1>
|
|
62
|
+
<div class="flex flex-wrap items-center gap-4 text-sm text-gray-400">
|
|
63
|
+
<span class="flex items-center gap-1.5">
|
|
64
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
65
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"/>
|
|
66
|
+
</svg>
|
|
67
|
+
<%= post.author %>
|
|
68
|
+
</span>
|
|
69
|
+
<span class="flex items-center gap-1.5">
|
|
70
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
71
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
|
72
|
+
</svg>
|
|
73
|
+
<%= post.date %>
|
|
74
|
+
</span>
|
|
75
|
+
<span class="flex items-center gap-1.5">
|
|
76
|
+
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
77
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
78
|
+
</svg>
|
|
79
|
+
<%= post.readTime %>
|
|
80
|
+
</span>
|
|
81
|
+
</div>
|
|
82
|
+
</header>
|
|
83
|
+
|
|
84
|
+
<!-- Post Body -->
|
|
85
|
+
<div id="post-content" class="bg-white rounded-2xl border border-gray-100 shadow-sm p-6 sm:p-8">
|
|
86
|
+
<!-- Markdown content will be rendered here -->
|
|
87
|
+
<div class="text-gray-400 text-sm">Loading content...</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<!-- Hidden raw markdown -->
|
|
91
|
+
<script id="raw-markdown" type="text/plain"><%= post.content %></script>
|
|
92
|
+
|
|
93
|
+
<!-- Share & Actions -->
|
|
94
|
+
<div class="flex items-center justify-between mt-6 pt-6 border-t border-gray-100">
|
|
95
|
+
<div class="text-sm text-gray-400">Share this article</div>
|
|
96
|
+
<div class="flex items-center gap-3">
|
|
97
|
+
<a
|
|
98
|
+
href="https://twitter.com/intent/tweet?text=<%= encodeURIComponent(post.title) %>&url=<%= encodeURIComponent('https://llmapi.pro/blog/' + post.slug) %>"
|
|
99
|
+
target="_blank"
|
|
100
|
+
rel="noopener noreferrer"
|
|
101
|
+
class="inline-flex items-center gap-2 px-3 py-1.5 text-sm text-gray-600 bg-white border border-gray-200 rounded-lg hover:border-claude-orange hover:text-claude-orange transition-colors"
|
|
102
|
+
>
|
|
103
|
+
<svg class="w-4 h-4" viewBox="0 0 24 24" fill="currentColor">
|
|
104
|
+
<path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z"/>
|
|
105
|
+
</svg>
|
|
106
|
+
Share on X
|
|
107
|
+
</a>
|
|
108
|
+
</div>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<!-- CTA Banner -->
|
|
112
|
+
<div class="mt-10 bg-gradient-to-r from-claude-dark to-gray-800 rounded-2xl p-8 text-white text-center">
|
|
113
|
+
<h3 class="text-xl font-bold mb-2">Start using LLM API</h3>
|
|
114
|
+
<p class="text-gray-300 mb-6">Free tier available. One-line configuration for Claude Code.</p>
|
|
115
|
+
<a href="/register" class="inline-flex items-center px-6 py-3 text-sm font-medium text-white bg-claude-orange rounded-xl hover:bg-opacity-90 transition-all shadow-md hover:shadow-lg">
|
|
116
|
+
Get Started Free
|
|
117
|
+
<svg class="ml-2 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
118
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"/>
|
|
119
|
+
</svg>
|
|
120
|
+
</a>
|
|
121
|
+
</div>
|
|
122
|
+
</article>
|
|
123
|
+
|
|
124
|
+
<!-- Sidebar -->
|
|
125
|
+
<aside class="lg:w-1/3">
|
|
126
|
+
<div class="sticky top-24 space-y-6">
|
|
127
|
+
|
|
128
|
+
<!-- Table of Contents -->
|
|
129
|
+
<div class="bg-white rounded-2xl border border-gray-100 shadow-sm p-6">
|
|
130
|
+
<h3 class="text-base font-semibold text-claude-dark mb-4">Table of Contents</h3>
|
|
131
|
+
<nav id="toc" class="space-y-1">
|
|
132
|
+
<div class="text-sm text-gray-400">Loading...</div>
|
|
133
|
+
</nav>
|
|
134
|
+
</div>
|
|
135
|
+
|
|
136
|
+
<!-- Related Posts -->
|
|
137
|
+
<% if (typeof recentPosts !== 'undefined' && recentPosts.length > 0) { %>
|
|
138
|
+
<div class="bg-white rounded-2xl border border-gray-100 shadow-sm p-6">
|
|
139
|
+
<h3 class="text-base font-semibold text-claude-dark mb-4">Related Posts</h3>
|
|
140
|
+
<ul class="space-y-4">
|
|
141
|
+
<% recentPosts.forEach(function(rp) { %>
|
|
142
|
+
<li>
|
|
143
|
+
<a href="/blog/<%= rp.slug %>" class="group block">
|
|
144
|
+
<span class="inline-block px-2 py-0.5 text-[10px] font-medium text-claude-orange bg-claude-orange/10 rounded-full mb-1"><%= rp.category %></span>
|
|
145
|
+
<h4 class="text-sm font-medium text-claude-dark group-hover:text-claude-orange transition-colors leading-snug">
|
|
146
|
+
<%= rp.title %>
|
|
147
|
+
</h4>
|
|
148
|
+
<span class="text-xs text-gray-400 mt-1 block"><%= rp.date %></span>
|
|
149
|
+
</a>
|
|
150
|
+
</li>
|
|
151
|
+
<% }); %>
|
|
152
|
+
</ul>
|
|
153
|
+
</div>
|
|
154
|
+
<% } %>
|
|
155
|
+
|
|
156
|
+
</div>
|
|
157
|
+
</aside>
|
|
158
|
+
|
|
159
|
+
</div>
|
|
160
|
+
</div>
|
|
161
|
+
</section>
|
|
162
|
+
|
|
163
|
+
<!-- Footer -->
|
|
164
|
+
<footer class="border-t border-gray-200 bg-white">
|
|
165
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
166
|
+
<div class="flex flex-col md:flex-row items-center justify-between gap-4">
|
|
167
|
+
<div class="flex items-center space-x-2">
|
|
168
|
+
<span class="text-lg font-bold text-claude-dark">LLM <span class="text-claude-orange">API</span></span>
|
|
169
|
+
</div>
|
|
170
|
+
<div class="flex items-center space-x-6 text-sm text-gray-500">
|
|
171
|
+
<a href="/pricing" class="hover:text-claude-orange transition-colors">Pricing</a>
|
|
172
|
+
<a href="/docs" class="hover:text-claude-orange transition-colors">Docs</a>
|
|
173
|
+
<a href="/blog" class="hover:text-claude-orange transition-colors">Blog</a>
|
|
174
|
+
<a href="/terms" class="hover:text-claude-orange transition-colors">Terms</a>
|
|
175
|
+
<a href="/privacy" class="hover:text-claude-orange transition-colors">Privacy</a>
|
|
176
|
+
</div>
|
|
177
|
+
<p class="text-sm text-gray-400">© <%= new Date().getFullYear() %> LLM API. All rights reserved.</p>
|
|
178
|
+
</div>
|
|
179
|
+
</div>
|
|
180
|
+
</footer>
|
|
181
|
+
|
|
182
|
+
<!-- Markdown Rendering & TOC Generation -->
|
|
183
|
+
<style>
|
|
184
|
+
/* Prose-like styling for rendered markdown */
|
|
185
|
+
#post-content h2 {
|
|
186
|
+
font-size: 1.5rem;
|
|
187
|
+
font-weight: 700;
|
|
188
|
+
margin-top: 2rem;
|
|
189
|
+
margin-bottom: 1rem;
|
|
190
|
+
color: #1A1915;
|
|
191
|
+
padding-bottom: 0.5rem;
|
|
192
|
+
border-bottom: 1px solid #f3f4f6;
|
|
193
|
+
}
|
|
194
|
+
#post-content h3 {
|
|
195
|
+
font-size: 1.25rem;
|
|
196
|
+
font-weight: 600;
|
|
197
|
+
margin-top: 1.5rem;
|
|
198
|
+
margin-bottom: 0.75rem;
|
|
199
|
+
color: #1A1915;
|
|
200
|
+
}
|
|
201
|
+
#post-content p {
|
|
202
|
+
margin-bottom: 1rem;
|
|
203
|
+
line-height: 1.75;
|
|
204
|
+
color: #4b5563;
|
|
205
|
+
}
|
|
206
|
+
#post-content a {
|
|
207
|
+
color: #D97757;
|
|
208
|
+
text-decoration: underline;
|
|
209
|
+
text-underline-offset: 2px;
|
|
210
|
+
}
|
|
211
|
+
#post-content a:hover {
|
|
212
|
+
color: #c4663f;
|
|
213
|
+
}
|
|
214
|
+
#post-content ul,
|
|
215
|
+
#post-content ol {
|
|
216
|
+
margin-bottom: 1rem;
|
|
217
|
+
padding-left: 1.5rem;
|
|
218
|
+
color: #4b5563;
|
|
219
|
+
}
|
|
220
|
+
#post-content ul {
|
|
221
|
+
list-style-type: disc;
|
|
222
|
+
}
|
|
223
|
+
#post-content ol {
|
|
224
|
+
list-style-type: decimal;
|
|
225
|
+
}
|
|
226
|
+
#post-content li {
|
|
227
|
+
margin-bottom: 0.375rem;
|
|
228
|
+
line-height: 1.75;
|
|
229
|
+
}
|
|
230
|
+
#post-content blockquote {
|
|
231
|
+
border-left: 4px solid #D97757;
|
|
232
|
+
padding: 0.75rem 1rem;
|
|
233
|
+
margin: 1.5rem 0;
|
|
234
|
+
background-color: #faf6f1;
|
|
235
|
+
color: #4b5563;
|
|
236
|
+
border-radius: 0 0.5rem 0.5rem 0;
|
|
237
|
+
}
|
|
238
|
+
#post-content blockquote p {
|
|
239
|
+
margin-bottom: 0;
|
|
240
|
+
}
|
|
241
|
+
#post-content code {
|
|
242
|
+
background-color: #f3f4f6;
|
|
243
|
+
color: #d97757;
|
|
244
|
+
padding: 0.125rem 0.375rem;
|
|
245
|
+
border-radius: 0.25rem;
|
|
246
|
+
font-size: 0.875rem;
|
|
247
|
+
font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;
|
|
248
|
+
}
|
|
249
|
+
#post-content pre {
|
|
250
|
+
background-color: #111827;
|
|
251
|
+
color: #4ade80;
|
|
252
|
+
padding: 1rem;
|
|
253
|
+
border-radius: 0.5rem;
|
|
254
|
+
overflow-x: auto;
|
|
255
|
+
margin: 1.5rem 0;
|
|
256
|
+
font-size: 0.875rem;
|
|
257
|
+
font-family: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, monospace;
|
|
258
|
+
}
|
|
259
|
+
#post-content pre code {
|
|
260
|
+
background-color: transparent;
|
|
261
|
+
color: inherit;
|
|
262
|
+
padding: 0;
|
|
263
|
+
border-radius: 0;
|
|
264
|
+
font-size: inherit;
|
|
265
|
+
}
|
|
266
|
+
#post-content table {
|
|
267
|
+
width: 100%;
|
|
268
|
+
border-collapse: collapse;
|
|
269
|
+
margin: 1.5rem 0;
|
|
270
|
+
font-size: 0.875rem;
|
|
271
|
+
}
|
|
272
|
+
#post-content thead th {
|
|
273
|
+
background-color: #f3f4f6;
|
|
274
|
+
font-weight: 600;
|
|
275
|
+
text-align: left;
|
|
276
|
+
padding: 0.625rem 0.75rem;
|
|
277
|
+
border: 1px solid #e5e7eb;
|
|
278
|
+
color: #1A1915;
|
|
279
|
+
}
|
|
280
|
+
#post-content tbody td {
|
|
281
|
+
padding: 0.625rem 0.75rem;
|
|
282
|
+
border: 1px solid #e5e7eb;
|
|
283
|
+
color: #4b5563;
|
|
284
|
+
}
|
|
285
|
+
#post-content tbody tr:hover {
|
|
286
|
+
background-color: #faf6f1;
|
|
287
|
+
}
|
|
288
|
+
#post-content img {
|
|
289
|
+
max-width: 100%;
|
|
290
|
+
height: auto;
|
|
291
|
+
border-radius: 0.5rem;
|
|
292
|
+
margin: 1.5rem 0;
|
|
293
|
+
}
|
|
294
|
+
#post-content hr {
|
|
295
|
+
border: none;
|
|
296
|
+
border-top: 1px solid #e5e7eb;
|
|
297
|
+
margin: 2rem 0;
|
|
298
|
+
}
|
|
299
|
+
#post-content strong {
|
|
300
|
+
font-weight: 600;
|
|
301
|
+
color: #1A1915;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/* TOC styling */
|
|
305
|
+
#toc a {
|
|
306
|
+
display: block;
|
|
307
|
+
padding: 0.375rem 0.75rem;
|
|
308
|
+
font-size: 0.8125rem;
|
|
309
|
+
color: #6b7280;
|
|
310
|
+
border-left: 2px solid transparent;
|
|
311
|
+
border-radius: 0 0.25rem 0.25rem 0;
|
|
312
|
+
transition: all 150ms;
|
|
313
|
+
text-decoration: none;
|
|
314
|
+
}
|
|
315
|
+
#toc a:hover,
|
|
316
|
+
#toc a.active {
|
|
317
|
+
color: #D97757;
|
|
318
|
+
border-left-color: #D97757;
|
|
319
|
+
background-color: rgba(217, 119, 87, 0.05);
|
|
320
|
+
}
|
|
321
|
+
</style>
|
|
322
|
+
|
|
323
|
+
<script>
|
|
324
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
325
|
+
var rawEl = document.getElementById('raw-markdown');
|
|
326
|
+
var contentEl = document.getElementById('post-content');
|
|
327
|
+
var tocEl = document.getElementById('toc');
|
|
328
|
+
|
|
329
|
+
if (!rawEl || !contentEl) return;
|
|
330
|
+
|
|
331
|
+
var md = rawEl.textContent || rawEl.innerText || '';
|
|
332
|
+
|
|
333
|
+
// Configure marked
|
|
334
|
+
if (typeof marked !== 'undefined') {
|
|
335
|
+
marked.setOptions({
|
|
336
|
+
breaks: false,
|
|
337
|
+
gfm: true
|
|
338
|
+
});
|
|
339
|
+
contentEl.innerHTML = marked.parse(md);
|
|
340
|
+
} else {
|
|
341
|
+
// Fallback: render raw text
|
|
342
|
+
contentEl.innerHTML = '<div class="text-gray-500">Content could not be rendered.</div>';
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Generate Table of Contents from h2 elements
|
|
346
|
+
var headings = contentEl.querySelectorAll('h2');
|
|
347
|
+
if (headings.length > 0) {
|
|
348
|
+
tocEl.innerHTML = '';
|
|
349
|
+
headings.forEach(function(h, i) {
|
|
350
|
+
var id = 'section-' + i;
|
|
351
|
+
h.setAttribute('id', id);
|
|
352
|
+
var link = document.createElement('a');
|
|
353
|
+
link.href = '#' + id;
|
|
354
|
+
link.textContent = h.textContent;
|
|
355
|
+
tocEl.appendChild(link);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// Highlight active TOC item on scroll
|
|
359
|
+
var tocLinks = tocEl.querySelectorAll('a');
|
|
360
|
+
var observer = new IntersectionObserver(function(entries) {
|
|
361
|
+
entries.forEach(function(entry) {
|
|
362
|
+
if (entry.isIntersecting) {
|
|
363
|
+
tocLinks.forEach(function(l) { l.classList.remove('active'); });
|
|
364
|
+
var activeLink = tocEl.querySelector('a[href="#' + entry.target.id + '"]');
|
|
365
|
+
if (activeLink) activeLink.classList.add('active');
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
}, { rootMargin: '-80px 0px -70% 0px', threshold: 0 });
|
|
369
|
+
|
|
370
|
+
headings.forEach(function(h) { observer.observe(h); });
|
|
371
|
+
} else {
|
|
372
|
+
tocEl.innerHTML = '<div class="text-sm text-gray-400">No sections found.</div>';
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
</script>
|
|
376
|
+
|
|
377
|
+
</body>
|
|
378
|
+
</html>
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
<%- include('../partials/header') %>
|
|
2
|
+
|
|
3
|
+
<%- include('../partials/nav') %>
|
|
4
|
+
|
|
5
|
+
<!-- Hero Section -->
|
|
6
|
+
<section class="relative overflow-hidden">
|
|
7
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 pt-16 pb-12 sm:pt-20 sm:pb-16">
|
|
8
|
+
<div class="text-center max-w-3xl mx-auto">
|
|
9
|
+
<div class="inline-flex items-center px-3 py-1 mb-6 text-xs font-medium text-claude-orange bg-claude-orange/10 rounded-full">
|
|
10
|
+
LLM API Blog
|
|
11
|
+
</div>
|
|
12
|
+
<h1 class="text-4xl sm:text-5xl font-bold text-claude-dark tracking-tight leading-tight">
|
|
13
|
+
Blog
|
|
14
|
+
</h1>
|
|
15
|
+
<p class="mt-4 text-lg text-gray-500 leading-relaxed">
|
|
16
|
+
Insights on AI coding, Claude Code optimization, and cost-effective development
|
|
17
|
+
</p>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="absolute top-0 left-1/2 -translate-x-1/2 w-[800px] h-[400px] bg-gradient-to-b from-claude-orange/5 to-transparent rounded-full blur-3xl -z-10"></div>
|
|
21
|
+
</section>
|
|
22
|
+
|
|
23
|
+
<!-- Blog Content -->
|
|
24
|
+
<section class="pb-20 sm:pb-24">
|
|
25
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
26
|
+
<div class="flex flex-col lg:flex-row gap-10">
|
|
27
|
+
|
|
28
|
+
<!-- Posts Grid -->
|
|
29
|
+
<div class="lg:w-2/3">
|
|
30
|
+
<% if (typeof posts !== 'undefined' && posts.length > 0) { %>
|
|
31
|
+
<div class="grid sm:grid-cols-2 gap-6">
|
|
32
|
+
<% posts.forEach(function(post) { %>
|
|
33
|
+
<article class="bg-white rounded-2xl border border-gray-100 shadow-sm hover:shadow-md transition-shadow overflow-hidden flex flex-col">
|
|
34
|
+
<div class="p-6 flex flex-col flex-1">
|
|
35
|
+
<div class="flex items-center gap-3 mb-3">
|
|
36
|
+
<span class="inline-block px-2.5 py-0.5 text-xs font-medium text-claude-orange bg-claude-orange/10 rounded-full">
|
|
37
|
+
<%= post.category %>
|
|
38
|
+
</span>
|
|
39
|
+
</div>
|
|
40
|
+
<h2 class="text-lg font-semibold text-claude-dark mb-2 leading-snug">
|
|
41
|
+
<a href="/blog/<%= post.slug %>" class="hover:text-claude-orange transition-colors">
|
|
42
|
+
<%= post.title %>
|
|
43
|
+
</a>
|
|
44
|
+
</h2>
|
|
45
|
+
<p class="text-sm text-gray-500 leading-relaxed mb-4 flex-1">
|
|
46
|
+
<%= post.description %>
|
|
47
|
+
</p>
|
|
48
|
+
<div class="flex items-center justify-between text-xs text-gray-400 pt-4 border-t border-gray-50">
|
|
49
|
+
<div class="flex items-center gap-3">
|
|
50
|
+
<span class="flex items-center gap-1">
|
|
51
|
+
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
52
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
|
53
|
+
</svg>
|
|
54
|
+
<%= post.date %>
|
|
55
|
+
</span>
|
|
56
|
+
<span class="flex items-center gap-1">
|
|
57
|
+
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
58
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
|
59
|
+
</svg>
|
|
60
|
+
<%= post.readTime %>
|
|
61
|
+
</span>
|
|
62
|
+
</div>
|
|
63
|
+
<span class="text-gray-400"><%= post.author %></span>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</article>
|
|
67
|
+
<% }); %>
|
|
68
|
+
</div>
|
|
69
|
+
<% } else { %>
|
|
70
|
+
<div class="text-center py-20">
|
|
71
|
+
<div class="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4">
|
|
72
|
+
<svg class="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
73
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"/>
|
|
74
|
+
</svg>
|
|
75
|
+
</div>
|
|
76
|
+
<p class="text-gray-500 text-lg">No posts yet. Check back soon!</p>
|
|
77
|
+
</div>
|
|
78
|
+
<% } %>
|
|
79
|
+
</div>
|
|
80
|
+
|
|
81
|
+
<!-- Sidebar -->
|
|
82
|
+
<aside class="lg:w-1/3">
|
|
83
|
+
<div class="sticky top-24 space-y-6">
|
|
84
|
+
|
|
85
|
+
<!-- Categories -->
|
|
86
|
+
<% if (typeof categories !== 'undefined' && categories.length > 0) { %>
|
|
87
|
+
<div class="bg-white rounded-2xl border border-gray-100 shadow-sm p-6">
|
|
88
|
+
<h3 class="text-base font-semibold text-claude-dark mb-4">Categories</h3>
|
|
89
|
+
<ul class="space-y-2">
|
|
90
|
+
<% categories.forEach(function(cat) { %>
|
|
91
|
+
<li>
|
|
92
|
+
<a href="/blog?category=<%= encodeURIComponent(cat.name) %>" class="flex items-center justify-between px-3 py-2 rounded-lg text-sm text-gray-600 hover:bg-claude-orange/5 hover:text-claude-orange transition-colors">
|
|
93
|
+
<span><%= cat.name %></span>
|
|
94
|
+
<span class="inline-flex items-center justify-center w-6 h-6 text-xs font-medium text-gray-400 bg-gray-100 rounded-full"><%= cat.count %></span>
|
|
95
|
+
</a>
|
|
96
|
+
</li>
|
|
97
|
+
<% }); %>
|
|
98
|
+
</ul>
|
|
99
|
+
</div>
|
|
100
|
+
<% } %>
|
|
101
|
+
|
|
102
|
+
<!-- Get Started CTA -->
|
|
103
|
+
<div class="bg-gradient-to-br from-claude-dark to-gray-800 rounded-2xl p-6 text-white">
|
|
104
|
+
<div class="w-10 h-10 bg-claude-orange/20 rounded-xl flex items-center justify-center mb-4">
|
|
105
|
+
<svg class="w-5 h-5 text-claude-orange" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
106
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
|
|
107
|
+
</svg>
|
|
108
|
+
</div>
|
|
109
|
+
<h3 class="text-lg font-semibold mb-2">Get Started with LLM API</h3>
|
|
110
|
+
<p class="text-sm text-gray-300 leading-relaxed mb-4">
|
|
111
|
+
Claude Code compatible API service. One-line configuration, reliable and fast.
|
|
112
|
+
</p>
|
|
113
|
+
<a href="/register" class="inline-flex items-center px-4 py-2.5 text-sm font-medium text-white bg-claude-orange rounded-xl hover:bg-opacity-90 transition-all shadow-sm hover:shadow">
|
|
114
|
+
Sign Up Free
|
|
115
|
+
<svg class="ml-2 w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
116
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"/>
|
|
117
|
+
</svg>
|
|
118
|
+
</a>
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
</div>
|
|
122
|
+
</aside>
|
|
123
|
+
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
</section>
|
|
127
|
+
|
|
128
|
+
<!-- Footer -->
|
|
129
|
+
<footer class="border-t border-gray-200 bg-white">
|
|
130
|
+
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-12">
|
|
131
|
+
<div class="flex flex-col md:flex-row items-center justify-between gap-4">
|
|
132
|
+
<div class="flex items-center space-x-2">
|
|
133
|
+
<span class="text-lg font-bold text-claude-dark">LLM <span class="text-claude-orange">API</span></span>
|
|
134
|
+
</div>
|
|
135
|
+
<div class="flex items-center space-x-6 text-sm text-gray-500">
|
|
136
|
+
<a href="/pricing" class="hover:text-claude-orange transition-colors">Pricing</a>
|
|
137
|
+
<a href="/docs" class="hover:text-claude-orange transition-colors">Docs</a>
|
|
138
|
+
<a href="/blog" class="hover:text-claude-orange transition-colors">Blog</a>
|
|
139
|
+
<a href="/terms" class="hover:text-claude-orange transition-colors">Terms</a>
|
|
140
|
+
<a href="/privacy" class="hover:text-claude-orange transition-colors">Privacy</a>
|
|
141
|
+
</div>
|
|
142
|
+
<p class="text-sm text-gray-400">© <%= new Date().getFullYear() %> LLM API. All rights reserved.</p>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
</footer>
|
|
146
|
+
|
|
147
|
+
</body>
|
|
148
|
+
</html>
|