blob_adminapp.tsx19 matches
70const menuRef = useRef(null);
71const isPublic = blob.key.startsWith("__public/");
72const publicUrl = isPublic ? `${window.location.origin}/api/public/${encodeURIComponent(blob.key.slice(9))}` : null;
7374useEffect(() => {
234setLoading(true);
235try {
236const response = await fetch(`/api/blobs?prefix=${encodeKey(searchPrefix)}&limit=${limit}`);
237const data = await response.json();
238setBlobs(data);
261setBlobContentLoading(true);
262try {
263const response = await fetch(`/api/blob?key=${encodeKey(clickedBlob.key)}`);
264const content = await response.text();
265setSelectedBlob({ ...clickedBlob, key: decodeKey(clickedBlob.key) });
275const handleSave = async () => {
276try {
277await fetch(`/api/blob?key=${encodeKey(selectedBlob.key)}`, {
278method: "PUT",
279body: editContent,
287const handleDelete = async (key) => {
288try {
289await fetch(`/api/blob?key=${encodeKey(key)}`, { method: "DELETE" });
290setBlobs(blobs.filter(b => b.key !== key));
291if (selectedBlob && selectedBlob.key === key) {
304const key = `${searchPrefix}${file.name}`;
305formData.append("key", encodeKey(key));
306await fetch("/api/blob", { method: "POST", body: formData });
307const newBlob = { key, size: file.size, lastModified: new Date().toISOString() };
308setBlobs([newBlob, ...blobs]);
326try {
327const fullKey = `${searchPrefix}${key}`;
328await fetch(`/api/blob?key=${encodeKey(fullKey)}`, {
329method: "PUT",
330body: "",
341const handleDownload = async (key) => {
342try {
343const response = await fetch(`/api/blob?key=${encodeKey(key)}`);
344const blob = await response.blob();
345const url = window.URL.createObjectURL(blob);
360if (newKey && newKey !== oldKey) {
361try {
362const response = await fetch(`/api/blob?key=${encodeKey(oldKey)}`);
363const content = await response.blob();
364await fetch(`/api/blob?key=${encodeKey(newKey)}`, {
365method: "PUT",
366body: content,
367});
368await fetch(`/api/blob?key=${encodeKey(oldKey)}`, { method: "DELETE" });
369setBlobs(blobs.map(b => b.key === oldKey ? { ...b, key: newKey } : b));
370if (selectedBlob && selectedBlob.key === oldKey) {
380const newKey = `__public/${key}`;
381try {
382const response = await fetch(`/api/blob?key=${encodeKey(key)}`);
383const content = await response.blob();
384await fetch(`/api/blob?key=${encodeKey(newKey)}`, {
385method: "PUT",
386body: content,
387});
388await fetch(`/api/blob?key=${encodeKey(key)}`, { method: "DELETE" });
389setBlobs(blobs.map(b => b.key === key ? { ...b, key: newKey } : b));
390if (selectedBlob && selectedBlob.key === key) {
399const newKey = key.slice(9); // Remove "__public/" prefix
400try {
401const response = await fetch(`/api/blob?key=${encodeKey(key)}`);
402const content = await response.blob();
403await fetch(`/api/blob?key=${encodeKey(newKey)}`, {
404method: "PUT",
405body: content,
406});
407await fetch(`/api/blob?key=${encodeKey(key)}`, { method: "DELETE" });
408setBlobs(blobs.map(b => b.key === key ? { ...b, key: newKey } : b));
409if (selectedBlob && selectedBlob.key === key) {
554onClick={() =>
555copyToClipboard(
556`${window.location.origin}/api/public/${encodeURIComponent(selectedBlob.key.slice(9))}`,
557)}
558className="text-blue-400 hover:text-blue-300 text-sm"
577>
578<img
579src={`/api/blob?key=${encodeKey(selectedBlob.key)}`}
580alt="Blob content"
581className="max-w-full h-auto"
2import { Hono } from "npm:hono";
34import { getLaunches } from "./spacexapi.ts";
5import { spacexCalendar } from "./spacexcalendar.ts";
6import { getChartUrl } from "./spacexchart.ts";
75{
76/* <div class="mb-3">
77<a href="/api" target="_blank">API</a>
78</div>
79<div class="mb-4">
1export default async (c, next) => {
2const secret = c.req.header("x-api-key");
3const method = c.req.method;
4if (secret !== Deno.env.get("X_API_KEY") && method !== "GET") {
5return c.text("Unauthorized", 401);
6}
8687/**
88* Format chat history for Anthropic API
89*/
90function formatChatHistoryForAI(history) {
314bot.on("message", async (ctx) => {
315try {
316// Get Anthropic API key from environment
317const apiKey = Deno.env.get("ANTHROPIC_API_KEY");
318if (!apiKey) {
319console.error("Anthropic API key is not configured.");
320ctx.reply(
321"I apologize, but I'm not properly configured at the moment. Please inform the household administrator.",
325326// Initialize Anthropic client
327const anthropic = new Anthropic({ apiKey });
328329// Get message text and user info
491// Set webhook if it is not set yet
492if (!isEndpointSet) {
493await bot.api.setWebhook(req.url, {
494secret_token: SECRET_TOKEN,
495});
166bot.on("message", async (ctx) => {
167try {
168const OPENAI_KEY = Deno.env.get("OPENAI_API_KEY");
169if (!OPENAI_KEY) {
170console.error("OPENAI_API_KEY is not configured.");
171await ctx.reply("I apologize, but I am not properly configured at present.");
172return;
173}
174175const openai = new OpenAI({ apiKey: OPENAI_KEY });
176177const messageText = ctx.message.text ?? "";
263export default async function(req: Request): Promise<Response> {
264if (!isEndpointSet) {
265await bot.api.setWebhook(req.url, { secret_token: SECRET_TOKEN });
266isEndpointSet = true;
267}
stevens-openaigenerateFunFacts.ts4 matches
66// -----------------------------------------------------------------------------
67async function generateFunFacts(previousFacts: { date: string; text: string }[]) {
68const apiKey = Deno.env.get("OPENAI_API_KEY");
69if (!apiKey) {
70console.error("OPENAI_API_KEY is not configured.");
71return [];
72}
7374const openai = new OpenAI({ apiKey });
7576// Build auxiliary strings for the prompt
stevens-openaigetWeather.ts4 matches
26*/
27async function generateConciseWeatherSummary(weatherDay: ReturnType<typeof summarizeWeather>[number]) {
28const apiKey = Deno.env.get("OPENAI_API_KEY");
29if (!apiKey) {
30console.error("OPENAI_API_KEY is not configured.");
31return null;
32}
3334const openai = new OpenAI({ apiKey });
3536const systemPrompt = `You are a weather forecaster. Create a very concise summary of a day's forecast.`;
stevens-openaisendDailyBrief.ts6 matches
96today?: DateTime,
97) {
98// Get API keys from environment
99const apiKey = Deno.env.get("OPENAI_API_KEY");
100const telegramToken = Deno.env.get("TELEGRAM_TOKEN");
101105}
106107if (!apiKey) {
108console.error("OPENAI_API_KEY is not configured.");
109return;
110}
121122// Initialize OpenAI client
123const openai = new OpenAI({ apiKey });
124125// Initialize Telegram bot
152153const sendChunk = async (chunk: string) => {
154await bot.api.sendMessage(chatId!, chunk, { parse_mode: "Markdown" });
155await storeChatMessage(chatId!, BOT_SENDER_ID, BOT_SENDER_NAME, chunk, true);
156};
stevens-openaiREADME.md1 match
53You'll need to set up some environment variables to make it run.
5455- `ANTHROPIC_API_KEY` for LLM calls
56- You'll need to follow [these instructions](https://docs.val.town/integrations/telegram/) to make a telegram bot, and set `TELEGRAM_TOKEN`. You'll also need to get a `TELEGRAM_CHAT_ID` in order to have the bot remember chat contents.
57- For the Google Calendar integration you'll need `GOOGLE_CALENDAR_ACCOUNT_ID` and `GOOGLE_CALENDAR_CALENDAR_ID`. See [these instuctions](https://www.val.town/v/stevekrouse/pipedream) for details.
stevens-openaiREADME.md5 matches
8## Hono
910This app uses [Hono](https://hono.dev/) as the API framework. You can think of Hono as a replacement for [ExpressJS](https://expressjs.com/) that works in serverless environments like Val Town or Cloudflare Workers. If you come from Python or Ruby, Hono is also a lot like [Flask](https://github.com/pallets/flask) or [Sinatra](https://github.com/sinatra/sinatra), respectively.
1112## Serving assets to the frontend
20### `index.html`
2122The most complicated part of this backend API is serving index.html. In this app (like most apps) we serve it at the root, ie `GET /`.
2324We *bootstrap* `index.html` with some initial data from the server, so that it gets dynamically injected JSON data without having to make another round-trip request to the server to get that data on the frontend. This is a common pattern for client-side rendered apps.
2526## CRUD API Routes
2728This app has two CRUD API routes: for reading and inserting into the messages table. They both speak JSON, which is standard. They import their functions from `/backend/database/queries.ts`. These routes are called from the React app to refresh and update data.
2930## Errors
3132Hono and other API frameworks have a habit of swallowing up Errors. We turn off this default behavior by re-throwing errors, because we think most of the time you'll want to see the full stack trace instead of merely "Internal Server Error". You can customize how you want errors to appear.