Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a6b857a
move db into services
jasonhedman Sep 20, 2025
090d48e
rename db client file
jasonhedman Sep 20, 2025
45f99df
move db operations into ops
jasonhedman Sep 20, 2025
d54ce66
update payouts services
jasonhedman Sep 20, 2025
5db4b2f
update referrals and emails
jasonhedman Sep 20, 2025
933e5e7
update providers db access
jasonhedman Sep 20, 2025
e8e6339
update auth db operations
jasonhedman Sep 20, 2025
0a291ca
move permissions into db app services
jasonhedman Sep 20, 2025
03e4cb5
move payment processing services
jasonhedman Sep 20, 2025
36a8905
check in
jasonhedman Sep 22, 2025
6cbe5b5
unify payment processing logic
jasonhedman Sep 22, 2025
cf6022d
remove payment intent success
jasonhedman Sep 22, 2025
a529c7d
check in stripe refac
jasonhedman Sep 22, 2025
6f0e99d
Merge branch 'master' into json/db-services
jasonhedman Sep 22, 2025
6404e3b
fix build
jasonhedman Sep 22, 2025
438017d
update lib folder name
jasonhedman Sep 22, 2025
622c78e
update dangling payments reference
jasonhedman Sep 22, 2025
05979ea
reorganize the db services folder
jasonhedman Sep 22, 2025
150549b
remove admin mint credits
jasonhedman Sep 22, 2025
4a38fc5
token logic in transactions
jasonhedman Sep 22, 2025
5aaf39a
import path for token metadata
jasonhedman Sep 22, 2025
b5ead18
redundant params to token response
jasonhedman Sep 22, 2025
c6bfbeb
remove unnecessary payment link reuse
jasonhedman Sep 22, 2025
e43f938
update create credit payment link schema
jasonhedman Sep 22, 2025
c3d46cf
optional successUrl
jasonhedman Sep 22, 2025
f6dc48e
update generated api types to use input instead of infer
jasonhedman Sep 22, 2025
f1c5a8c
rm text content type
jasonhedman Sep 22, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions packages/app/control/docs/advanced/api-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ List all Echo apps owned by the authenticated user.
```

<AutoTypeTable
path="src/services/apps/list.ts"
path="src/services/db/apps/list.ts"
type={`Awaited<ReturnType<typeof listOwnerApps>>["items"][number]`}
/>

Expand All @@ -83,7 +83,7 @@ Get detailed app information for programmatic access.
**Response Type:**

<AutoTypeTable
path="src/services/user/balance.ts"
path="src/services/db/user/balance.ts"
type={`Awaited<ReturnType<typeof getUserGlobalBalance>>`}
/>

Expand All @@ -105,7 +105,7 @@ Get authenticated user's credit balance.
**Response Type:**

<AutoTypeTable
path="src/services/user/balance.ts"
path="src/services/db/user/balance.ts"
type={`Awaited<ReturnType<typeof getUserGlobalBalance>>`}
/>

Expand All @@ -129,8 +129,8 @@ Get user's free tier spending information for a specific app.
```

<AutoTypeTable
path="src/lib/spend-pools/fetch-user-spend.ts"
type={`Awaited<ReturnType<typeof getCustomerSpendInfoForApp>>["userSpendInfo"]`}
path="src/services/db/user/app-spend-pool.ts"
type={`Awaited<ReturnType<typeof getUserSpendInfoForApp>>["userSpendInfo"]`}
/>

## Payments
Expand Down
60 changes: 60 additions & 0 deletions packages/app/control/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,69 @@ export default tseslint.config(
},
},
},
'no-db-client-outside-db': {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yea 😎

rules: {
'no-db-client-outside-db': {
meta: {
type: 'problem',
docs: {
description:
'Disallow @/services/db/client imports outside of @db/ folder',
category: 'Best Practices',
recommended: true,
},
fixable: null,
schema: [],
messages: {
noDbClientOutsideDb:
'Direct imports from @/services/db/client are not allowed outside of the @/services/db/ folder. Create a new service in @/services/db instead.',
},
},
create(context) {
const filename = context.getFilename();

// Allow imports within the @db/ folder structure
const isDbFolderFile = filename.includes('/services/db/');

// Allow imports in the client.ts file itself
const isClientFile = filename.includes('/services/db/client.ts');

if (isDbFolderFile || isClientFile) {
return {};
}

return {
ImportDeclaration(node) {
// Check if the import source is @/services/db/client or relative imports to services/db/client
if (
node.source &&
node.source.type === 'Literal' &&
typeof node.source.value === 'string'
) {
const importPath = node.source.value;
const isDbClientImport =
importPath === '@/services/db/client' ||
importPath.endsWith('/db/client') ||
importPath.endsWith('./services/db/client') ||
importPath.endsWith('../services/db/client');

if (isDbClientImport) {
context.report({
node,
messageId: 'noDbClientOutsideDb',
});
}
}
},
};
},
},
},
},
},
rules: {
'no-process-env-outside-env/no-process-env-outside-env': 'error',
'no-db-client-outside-db/no-db-client-outside-db': 'error',
},
}
);
3 changes: 2 additions & 1 deletion packages/app/control/scripts/seed-app-usage.ts

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ignore lint for seeder scripts. Don't really care about cleanliness here. Same goes for the next few files

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env tsx

import { db } from '../src/lib/db';
// eslint-disable-next-line no-db-client-outside-db/no-db-client-outside-db
import { db } from '../src/services/db/client';
import { faker } from '@faker-js/faker';
import { subDays, addDays, format } from 'date-fns';

Expand Down
3 changes: 2 additions & 1 deletion packages/app/control/scripts/seed-markup-rewards.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env tsx

import { db } from '../src/lib/db';
// eslint-disable-next-line no-db-client-outside-db/no-db-client-outside-db
import { db } from '../src/services/db/client';
import { faker } from '@faker-js/faker';
import { addDays, subDays, format } from 'date-fns';

Expand Down
3 changes: 2 additions & 1 deletion packages/app/control/scripts/seed-referral-rewards.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env tsx

import { db } from '../src/lib/db';
// eslint-disable-next-line no-db-client-outside-db/no-db-client-outside-db
import { db } from '../src/services/db/client';
import { faker } from '@faker-js/faker';
import { addDays, subDays, format } from 'date-fns';

Expand Down
3 changes: 2 additions & 1 deletion packages/app/control/scripts/seed-users.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env tsx

import { db } from '../src/lib/db';
// eslint-disable-next-line no-db-client-outside-db/no-db-client-outside-db
import { db } from '../src/services/db/client';
import { faker } from '@faker-js/faker';

interface SeedUsersOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
import { Skeleton } from '@/components/ui/skeleton';

import { useFiltersContext } from '../../contexts/filters-context';
import { FeedActivityType } from '@/services/feed/types';
import { FeedActivityType } from '@/services/db/feed/types';

export const EventTypesFilter = () => {
const { eventType, setEventType } = useFiltersContext();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { TransactionContent } from './transactions';
import { SignInContent } from './sign-in';

import type { FeedActivity } from '@/services/feed/types';
import { FeedActivityType } from '@/services/feed/types';
import type { FeedActivity } from '@/services/db/feed/types';
import { FeedActivityType } from '@/services/db/feed/types';

interface Props {
activity: FeedActivity;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { SignInEventData } from '@/services/feed/types';
import type { SignInEventData } from '@/services/db/feed/types';
import { User, Users } from 'lucide-react';

interface Props {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { formatCurrency } from '@/lib/utils';
import type { TransactionEventData } from '@/services/feed/types';
import type { TransactionEventData } from '@/services/db/feed/types';
import { DollarSign } from 'lucide-react';

interface Props {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { UserAvatar } from '@/components/utils/user-avatar';

import { ItemContent } from './content';

import type { FeedActivity } from '@/services/feed/types';
import type { FeedActivity } from '@/services/db/feed/types';

interface Props {
activity: FeedActivity;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { createContext, useContext, useEffect, useState } from 'react';
import { subDays } from 'date-fns';

import { ActivityTimeframe } from '@/types/timeframes';
import type { FeedActivityType } from '@/services/feed/types';
import type { FeedActivityType } from '@/services/db/feed/types';

interface FiltersContextType {
appId: string | undefined;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { TransactionContent } from './transactions';
import { SignInContent } from './sign-in';

import type { FeedActivity } from '@/services/feed/types';
import { FeedActivityType } from '@/services/feed/types';
import type { FeedActivity } from '@/services/db/feed/types';
import { FeedActivityType } from '@/services/db/feed/types';

interface Props {
activity: FeedActivity;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { SignInEventData } from '@/services/feed/types';
import type { SignInEventData } from '@/services/db/feed/types';

interface Props {
activity: SignInEventData;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { formatCurrency } from '@/lib/utils';
import type { TransactionEventData } from '@/services/feed/types';
import type { TransactionEventData } from '@/services/db/feed/types';

interface Props {
numUsers: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
AvatarCirclesSkeleton,
} from '@/components/ui/avatar-circles';
import { UserAvatar } from '@/components/utils/user-avatar';
import type { FeedActivity } from '@/services/feed/types';
import type { FeedActivity } from '@/services/db/feed/types';
import { formatDistanceToNow } from 'date-fns';
import { Code } from 'lucide-react';
import { ItemContent } from './content';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { Separator } from '@/components/ui/separator';

import { api } from '@/trpc/client';
import { MarkupInput } from '../../../_components/markup/input';
import { createAppSchema } from '@/services/apps/lib/schemas';
import { createAppSchema } from '@/services/db/apps/lib/schemas';

export const CreateAppForm = () => {
const form = useForm<z.infer<typeof createAppSchema>>({
Expand Down

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use types from TRPC

Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,13 @@ export function CompletedPayoutsTable({ pageSize = 10 }: Props) {
const [page, setPage] = useState(0);

const { data, isLoading, isFetching } =
api.admin.payouts.listCompleted.useQuery({
api.admin.payouts.list.completed.useQuery({
cursor: page,
page_size: pageSize,
});

if (isLoading) return <div>Loading completed payouts...</div>;

const payouts = (data?.items ?? []) as Array<{
id: string;
amount: number;
createdAt: string;
type: string;
userEmail: string | null;
userName: string | null;
echoAppName: string | null;
recipientGithubUrl: string | null;
recipientAddress: string | null;
transactionId: string | null;
senderAddress: string | null;
}>;

return (
<div className="space-y-4">
<div className="flex items-center justify-between">
Expand All @@ -55,38 +41,38 @@ export function CompletedPayoutsTable({ pageSize = 10 }: Props) {
</tr>
</thead>
<tbody>
{payouts.map(p => (
{data?.items.map(p => (
<tr key={p.id} className="border-t">
<td className="p-3 whitespace-nowrap">
{new Date(p.createdAt).toLocaleString()}
</td>
<td className="p-3 capitalize">{p.type}</td>
<td className="p-3">${p.amount.toFixed(2)}</td>
<td className="p-3">
{p.userEmail ? (
{p.user?.email ? (
<div className="flex flex-col">
<span>{p.userName ?? '—'}</span>
<span>{p.user.name ?? '—'}</span>
<span className="text-muted-foreground">
{p.userEmail}
{p.user.email}
</span>
</div>
) : (
'—'
)}
</td>
<td className="p-3">{p.echoAppName ?? '—'}</td>
<td className="p-3">{p.echoApp?.name ?? '—'}</td>
<td className="p-3">
{p.recipientGithubUrl ? (
{p.recipientGithubLink?.githubUrl ? (
<a
href={p.recipientGithubUrl}
href={p.recipientGithubLink.githubUrl}
target="_blank"
rel="noreferrer"
className="text-primary underline"
>
GitHub Link
</a>
) : (
(p.recipientAddress ?? '—')
(p.recipientGithubLink?.githubUrl ?? '—')
)}
</td>
<td className="p-3">
Expand Down

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export function PendingPayoutsTable({ pageSize = 10 }: Props) {
const [page, setPage] = useState(0);

const { data, isLoading, refetch, isFetching } =
api.admin.payouts.listPending.useQuery({
api.admin.payouts.list.pending.useQuery({
cursor: page,
page_size: pageSize,
});
Expand All @@ -38,18 +38,6 @@ export function PendingPayoutsTable({ pageSize = 10 }: Props) {

if (isLoading) return <div>Loading pending payouts...</div>;

const payouts = (data?.items ?? []) as Array<{
id: string;
amount: number;
createdAt: string;
type: string;
userEmail: string | null;
userName: string | null;
echoAppName: string | null;
recipientGithubUrl: string | null;
recipientAddress: string | null;
}>;

return (
<div className="space-y-4">
<div className="flex items-center justify-between">
Expand Down Expand Up @@ -84,38 +72,38 @@ export function PendingPayoutsTable({ pageSize = 10 }: Props) {
</tr>
</thead>
<tbody>
{payouts.map(p => (
{data?.items.map(p => (
<tr key={p.id} className="border-t">
<td className="p-3 whitespace-nowrap">
{new Date(p.createdAt).toLocaleString()}
</td>
<td className="p-3 capitalize">{p.type}</td>
<td className="p-3">${p.amount.toFixed(2)}</td>
<td className="p-3">
{p.userEmail ? (
{p.user?.email ? (
<div className="flex flex-col">
<span>{p.userName ?? '—'}</span>
<span>{p.user.name ?? '—'}</span>
<span className="text-muted-foreground">
{p.userEmail}
{p.user.email}
</span>
</div>
) : (
'—'
)}
</td>
<td className="p-3">{p.echoAppName ?? '—'}</td>
<td className="p-3">{p.echoApp?.name ?? '—'}</td>
<td className="p-3">
{p.recipientGithubUrl ? (
{p.recipientGithubLink?.githubUrl ? (
<a
href={p.recipientGithubUrl}
href={p.recipientGithubLink.githubUrl}
target="_blank"
rel="noreferrer"
className="text-primary underline"
>
GitHub Link
</a>
) : (
(p.recipientAddress ?? '—')
(p.recipientGithubLink?.githubUrl ?? '—')
)}
</td>
<td className="p-3">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { toast } from 'sonner';
import type { RouterOutputs } from '@/trpc/client';
import { api } from '@/trpc/client';

import type { adminCreateCreditGrantSchema } from '@/services/admin/schemas';
import type { adminCreateCreditGrantSchema } from '@/services/db/admin/schemas';
import { CreditGrantForm } from '../../../_components/form';
import { revalidateCodePage } from '../_actions/revalidate';

Expand Down
Loading
Loading