Skip to content

Commit

Permalink
feat: support for anonymous Apollo key usage
Browse files Browse the repository at this point in the history
  • Loading branch information
samjcombs committed Nov 29, 2024
1 parent 81d3ce4 commit 66f2adc
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 28 deletions.
35 changes: 16 additions & 19 deletions backend/src/routes/apolloApiKey.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import express, {Response} from 'express';
import express, {Response, Request} from 'express';
import {DI} from '../server';
import {logger} from '../utilities/logger';
import {ApolloApiKey} from '../models/apolloApiKey';
import {SessionRequest} from 'supertokens-node/framework/express';
import config from '../config/config';

const router = express.Router();

router.get('/apollo-api-key', async (req: SessionRequest, res: Response) => {
try {
const userId = req.session?.getUserId();
if (!userId) {
return res.status(401).json({error: 'Unauthorized'});
}
const handleRequest = async (req: Request | SessionRequest, defaultUserId = 'anonymous') => {
const userId = (req as SessionRequest).session?.getUserId() || defaultUserId;
return userId;
};

router.get('/apollo-api-key', async (req: Request | SessionRequest, res: Response) => {
try {
const userId = await handleRequest(req);
const apiKey = await DI.apolloApiKeys.findOne({userId});

if (apiKey) {
const decryptedKey = apiKey.getDecryptedKey();
const obfuscatedKey = `${decryptedKey.slice(0, 4)}****${decryptedKey.slice(-4)}`;
Expand All @@ -27,14 +30,11 @@ router.get('/apollo-api-key', async (req: SessionRequest, res: Response) => {
}
});

router.post('/apollo-api-key', async (req: SessionRequest, res: Response) => {
router.post('/apollo-api-key', async (req: Request | SessionRequest, res: Response) => {
try {
const userId = req.session?.getUserId();
if (!userId) {
return res.status(401).json({error: 'Unauthorized'});
}

const userId = await handleRequest(req);
const {key} = req.body;

if (!key) {
return res.status(400).json({error: 'API key is required'});
}
Expand All @@ -59,14 +59,11 @@ router.post('/apollo-api-key', async (req: SessionRequest, res: Response) => {
}
});

router.delete('/apollo-api-key', async (req: SessionRequest, res: Response) => {
router.delete('/apollo-api-key', async (req: Request | SessionRequest, res: Response) => {
try {
const userId = req.session?.getUserId();
if (!userId) {
return res.status(401).json({error: 'Unauthorized'});
}

const userId = await handleRequest(req);
const apiKey = await DI.apolloApiKeys.findOne({userId});

if (!apiKey) {
return res.status(404).json({error: 'API key not found'});
}
Expand Down
15 changes: 11 additions & 4 deletions backend/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import './loadEnv';
import 'reflect-metadata';
import {EntityManager, EntityRepository, MikroORM, RequestContext,} from '@mikro-orm/core';
import {
EntityManager,
EntityRepository,
MikroORM,
RequestContext,
} from '@mikro-orm/core';
import cors from 'cors';
import express from 'express';
import path from 'path';
Expand Down Expand Up @@ -80,7 +85,7 @@ const initializeApp = async () => {
const mikroOrmConfig = {
...(await import(
`./mikro-orm.${process.env.MIKRO_ORM_DRIVER || 'sqlite'}${isTypescript ? '.ts' : '.js'}`
).then((module) => module.default)),
).then((module) => module.default)),
};

DI.orm = await MikroORM.init(mikroOrmConfig);
Expand Down Expand Up @@ -117,8 +122,10 @@ const initializeApp = async () => {
cors({
origin: (origin, callback) => {
const allowedOrigins = [getWebsiteDomain()];
const regex = /^(https:\/\/[a-zA-Z0-9-]+\.narrative\.tech|https?:\/\/localhost(:\d+)?)$/;
if (!origin || allowedOrigins.includes(origin) || regex.test(origin)) callback(null, true);
const regex =
/^(https:\/\/[a-zA-Z0-9-]+\.narrative\.tech|https?:\/\/localhost(:\d+)?)$/;
if (!origin || allowedOrigins.includes(origin) || regex.test(origin))
callback(null, true);
else callback(new Error('Not allowed by CORS'));
},
allowedHeaders: [
Expand Down
4 changes: 2 additions & 2 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 15 additions & 2 deletions frontend/src/components/ui/home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ import {Tabs, TabsContent, TabsList, TabsTrigger} from './tabs';
import {Textarea} from './textarea';
import {Toaster} from './toaster';
import {toast} from './use-toast';
import {Badge} from './badge';
import {SettingsBadge} from './settings-badge';

const Home = () => {
const navigate = useNavigate();
Expand Down Expand Up @@ -121,6 +123,17 @@ const Home = () => {

const serverBaseUrl = getApiBaseUrl();

const [hasApolloKey, setHasApolloKey] = useState(true);

useEffect(() => {
fetch(`${serverBaseUrl}/api/apollo-api-key`)
.then((response) => response.json())
.then((data) => {
setHasApolloKey(!!data.key);
})
.catch(() => setHasApolloKey(false));
}, [serverBaseUrl]);

const handleSettingsClick = () => navigate('/settings');
const handleCreateSeedClick = () => {
populateSeedForm();
Expand Down Expand Up @@ -527,8 +540,8 @@ const Home = () => {
</div>

<div className="flex items-center gap-4">
<Settings
className="h-5 w-5 text-gray-500 cursor-pointer"
<SettingsBadge
hasApolloKey={hasApolloKey}
onClick={handleSettingsClick}
/>
<ConditionalLoginDropdown />
Expand Down
51 changes: 51 additions & 0 deletions frontend/src/components/ui/settings-badge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {cn} from '../../lib/utils';
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from './tooltip';
import {Settings} from 'lucide-react';

interface SettingsBadgeProps {
hasApolloKey: boolean;
onClick: () => void;
}

export function SettingsBadge({hasApolloKey, onClick}: SettingsBadgeProps) {
return (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<div className="relative group cursor-pointer" onClick={onClick}>
<Settings className="h-5 w-5 text-gray-500 transition-colors group-hover:text-gray-900" />
{!hasApolloKey && (
<span
className={cn(
'absolute -top-0.5 -right-0.5 w-2 h-2 rounded-full',
'bg-red-500/90 transition-all duration-300',
'group-hover:scale-125 group-hover:bg-red-600',
'animate-[pulse_3s_ease-in-out_infinite]'
)}
style={{
animation: 'pulse 3s cubic-bezier(0.4, 0, 0.6, 1) infinite',
background: 'linear-gradient(45deg, #ef4444, #dc2626)',
}}
/>
)}
</div>
</TooltipTrigger>
<TooltipContent side="bottom" className="max-w-[300px]">
<p className="text-sm font-medium">
Connect to Apollo Studio
<span className="block text-xs font-normal text-muted-foreground mt-1">
Enter your user-scoped Apollo API key to instantly build custom
mock seeds against existing supergraph variants and schema
proposals
</span>
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
);
}
3 changes: 3 additions & 0 deletions frontend/src/components/ui/settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
} from './card';
import {Input} from './input';
import {useToast} from './use-toast';
import {useNavigate} from 'react-router-dom';

interface ApiKey {
id: string;
Expand All @@ -28,6 +29,7 @@ export default function SettingsPage() {
const [isLoading, setIsLoading] = useState(false);
const {toast} = useToast();
const serverBaseUrl = getApiBaseUrl();
const navigate = useNavigate();

useEffect(() => {
fetchApiKey();
Expand Down Expand Up @@ -65,6 +67,7 @@ export default function SettingsPage() {
title: 'Apollo API Key Saved',
description: 'Your Apollo API key has been saved successfully.',
});
navigate('/');
}
});
};
Expand Down
6 changes: 5 additions & 1 deletion frontend/tailwind.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ module.exports = {
to: {
height: '0'
}
}
},
pulse: {
'0%, 100%': { opacity: '0.7', transform: 'scale(1)' },
'50%': { opacity: '0.3', transform: 'scale(0.95)' },
},
},
animation: {
'accordion-down': 'accordion-down 0.2s ease-out',
Expand Down

0 comments on commit 66f2adc

Please sign in to comment.