mirror of
https://github.com/MAGICGrants/campaign-site.git
synced 2026-01-08 20:08:05 -05:00
@@ -1,5 +1,4 @@
|
||||
// .dockerignore
|
||||
node_modules
|
||||
build
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
|
||||
@@ -58,4 +58,6 @@ ATTESTATION_PRIVATE_KEY=""
|
||||
NEXT_PUBLIC_ATTESTATION_PUBLIC_KEY=""
|
||||
|
||||
COINBASE_COMMERCE_API_KEY=""
|
||||
COINBASE_COMMERCE_WEBHOOK_SECRET=""
|
||||
COINBASE_COMMERCE_WEBHOOK_SECRET=""
|
||||
|
||||
SENTRY_AUTH_TOKEN=""
|
||||
1
.github/workflows/deploy.yml
vendored
1
.github/workflows/deploy.yml
vendored
@@ -59,5 +59,6 @@ jobs:
|
||||
ATTESTATION_PRIVATE_KEY_HEX=${{ secrets.ATTESTATION_PRIVATE_KEY_HEX }} \
|
||||
COINBASE_COMMERCE_API_KEY=${{ secrets.COINBASE_COMMERCE_API_KEY }} \
|
||||
COINBASE_COMMERCE_WEBHOOK_SECRET=${{ secrets.COINBASE_COMMERCE_WEBHOOK_SECRET }} \
|
||||
SENTRY_AUTH_TOKEN=${{ secrets.SENTRY_AUTH_TOKEN }} \
|
||||
docker compose -f docker-compose.prod.yml up -d --build
|
||||
EOF
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -37,3 +37,6 @@ yarn-error.log*
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
|
||||
# Sentry Config File
|
||||
.env.sentry-build-plugin
|
||||
|
||||
@@ -8,11 +8,11 @@ function BitcoinLogo(props: SVGProps<SVGSVGElement>) {
|
||||
width="100%"
|
||||
height="100%"
|
||||
version="1.1"
|
||||
shape-rendering="geometricPrecision"
|
||||
text-rendering="geometricPrecision"
|
||||
image-rendering="optimizeQuality"
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
shapeRendering="geometricPrecision"
|
||||
textRendering="geometricPrecision"
|
||||
imageRendering="optimizeQuality"
|
||||
fillRule="evenodd"
|
||||
clipRule="evenodd"
|
||||
viewBox="0 0 4091.27 4091.73"
|
||||
xmlnsXlink="http://www.w3.org/1999/xlink"
|
||||
{...props}
|
||||
@@ -22,12 +22,12 @@ function BitcoinLogo(props: SVGProps<SVGSVGElement>) {
|
||||
<g id="_1421344023328">
|
||||
<path
|
||||
fill="#F7931A"
|
||||
fill-rule="nonzero"
|
||||
fillRule="nonzero"
|
||||
d="M4030.06 2540.77c-273.24,1096.01 -1383.32,1763.02 -2479.46,1489.71 -1095.68,-273.24 -1762.69,-1383.39 -1489.33,-2479.31 273.12,-1096.13 1383.2,-1763.19 2479,-1489.95 1096.06,273.24 1763.03,1383.51 1489.76,2479.57l0.02 -0.02z"
|
||||
/>
|
||||
<path
|
||||
fill="white"
|
||||
fill-rule="nonzero"
|
||||
fillRule="nonzero"
|
||||
d="M2947.77 1754.38c40.72,-272.26 -166.56,-418.61 -450,-516.24l91.95 -368.8 -224.5 -55.94 -89.51 359.09c-59.02,-14.72 -119.63,-28.59 -179.87,-42.34l90.16 -361.46 -224.36 -55.94 -92 368.68c-48.84,-11.12 -96.81,-22.11 -143.35,-33.69l0.26 -1.16 -309.59 -77.31 -59.72 239.78c0,0 166.56,38.18 163.05,40.53 90.91,22.69 107.35,82.87 104.62,130.57l-104.74 420.15c6.26,1.59 14.38,3.89 23.34,7.49 -7.49,-1.86 -15.46,-3.89 -23.73,-5.87l-146.81 588.57c-11.11,27.62 -39.31,69.07 -102.87,53.33 2.25,3.26 -163.17,-40.72 -163.17,-40.72l-111.46 256.98 292.15 72.83c54.35,13.63 107.61,27.89 160.06,41.3l-92.9 373.03 224.24 55.94 92 -369.07c61.26,16.63 120.71,31.97 178.91,46.43l-91.69 367.33 224.51 55.94 92.89 -372.33c382.82,72.45 670.67,43.24 791.83,-303.02 97.63,-278.78 -4.86,-439.58 -206.26,-544.44 146.69,-33.83 257.18,-130.31 286.64,-329.61l-0.07 -0.05zm-512.93 719.26c-69.38,278.78 -538.76,128.08 -690.94,90.29l123.28 -494.2c152.17,37.99 640.17,113.17 567.67,403.91zm69.43 -723.3c-63.29,253.58 -453.96,124.75 -580.69,93.16l111.77 -448.21c126.73,31.59 534.85,90.55 468.94,355.05l-0.02 0z"
|
||||
/>
|
||||
</g>
|
||||
|
||||
@@ -30,37 +30,37 @@ function EvmIcon(props: SVGProps<SVGSVGElement>) {
|
||||
<g id="g6">
|
||||
<polygon
|
||||
fill="#343434"
|
||||
fill-rule="nonzero"
|
||||
fillRule="nonzero"
|
||||
points="383.5,29.11 383.5,873.74 392.07,882.29 784.13,650.54 392.07,0 "
|
||||
id="polygon1"
|
||||
/>
|
||||
<polygon
|
||||
fill="#8c8c8c"
|
||||
fill-rule="nonzero"
|
||||
fillRule="nonzero"
|
||||
points="392.07,472.33 392.07,0 0,650.54 392.07,882.29 "
|
||||
id="polygon2"
|
||||
/>
|
||||
<polygon
|
||||
fill="#3c3c3b"
|
||||
fill-rule="nonzero"
|
||||
fillRule="nonzero"
|
||||
points="387.24,962.41 387.24,1263.28 392.07,1277.38 784.37,724.89 392.07,956.52 "
|
||||
id="polygon3"
|
||||
/>
|
||||
<polygon
|
||||
fill="#8c8c8c"
|
||||
fill-rule="nonzero"
|
||||
fillRule="nonzero"
|
||||
points="392.07,956.52 0,724.89 392.07,1277.38 "
|
||||
id="polygon4"
|
||||
/>
|
||||
<polygon
|
||||
fill="#141414"
|
||||
fill-rule="nonzero"
|
||||
fillRule="nonzero"
|
||||
points="784.13,650.54 392.07,472.33 392.07,882.29 "
|
||||
id="polygon5"
|
||||
/>
|
||||
<polygon
|
||||
fill="#393939"
|
||||
fill-rule="nonzero"
|
||||
fillRule="nonzero"
|
||||
points="392.07,882.29 392.07,472.33 0,650.54 "
|
||||
id="polygon6"
|
||||
/>
|
||||
|
||||
@@ -85,6 +85,8 @@ services:
|
||||
|
||||
COINBASE_COMMERCE_API_KEY: ${COINBASE_COMMERCE_API_KEY}
|
||||
COINBASE_COMMERCE_WEBHOOK_SECRET: ${COINBASE_COMMERCE_WEBHOOK_SECRET}
|
||||
|
||||
SENTRY_AUTH_TOKEN: ${SENTRY_AUTH_TOKEN}
|
||||
depends_on:
|
||||
- magic-postgres
|
||||
networks:
|
||||
|
||||
@@ -113,7 +113,7 @@ services:
|
||||
container_name: magic-app
|
||||
restart: unless-stopped
|
||||
working_dir: /app
|
||||
command: sh -c 'apk add --no-cache libc6-compat && npm run dev'
|
||||
command: sh -c 'apk add --no-cache libc6-compat && npm i && npm run dev'
|
||||
ports:
|
||||
- 3000:3000
|
||||
volumes:
|
||||
|
||||
18
instrumentation-client.ts
Normal file
18
instrumentation-client.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// This file configures the initialization of Sentry on the client.
|
||||
// The added config here will be used whenever a users loads a page in their browser.
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
|
||||
import * as Sentry from '@sentry/nextjs'
|
||||
|
||||
if (process.env.NODE_ENV === 'production')
|
||||
Sentry.init({
|
||||
dsn: 'https://029796cccc72916ee470432a28927103@o4509249375436800.ingest.us.sentry.io/4509249381400576',
|
||||
|
||||
// Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
|
||||
tracesSampleRate: 1,
|
||||
|
||||
// Setting this option to true will print useful information to the console while you're setting up Sentry.
|
||||
debug: false,
|
||||
})
|
||||
|
||||
export const onRouterTransitionStart = Sentry.captureRouterTransitionStart
|
||||
13
instrumentation.ts
Normal file
13
instrumentation.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import * as Sentry from '@sentry/nextjs'
|
||||
|
||||
export async function register() {
|
||||
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
||||
await import('./sentry.server.config')
|
||||
}
|
||||
|
||||
if (process.env.NEXT_RUNTIME === 'edge') {
|
||||
await import('./sentry.edge.config')
|
||||
}
|
||||
}
|
||||
|
||||
export const onRequestError = Sentry.captureRequestError
|
||||
@@ -18,3 +18,39 @@ const nextConfig = {
|
||||
}
|
||||
|
||||
module.exports = nextConfig
|
||||
|
||||
// Injected content via Sentry wizard below
|
||||
|
||||
const { withSentryConfig } = require('@sentry/nextjs')
|
||||
|
||||
module.exports = withSentryConfig(module.exports, {
|
||||
// For all available options, see:
|
||||
// https://www.npmjs.com/package/@sentry/webpack-plugin#options
|
||||
|
||||
org: 'campaign-site',
|
||||
project: 'magic-grants',
|
||||
|
||||
// Only print logs for uploading source maps in CI
|
||||
silent: !process.env.CI,
|
||||
|
||||
// For all available options, see:
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
|
||||
|
||||
// Upload a larger set of source maps for prettier stack traces (increases build time)
|
||||
widenClientFileUpload: true,
|
||||
|
||||
// Route browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers.
|
||||
// This can increase your server load as well as your hosting bill.
|
||||
// Note: Check that the configured route will not match with your Next.js middleware, otherwise reporting of client-
|
||||
// side errors will fail.
|
||||
tunnelRoute: '/monitoring',
|
||||
|
||||
// Automatically tree-shake Sentry logger statements to reduce bundle size
|
||||
disableLogger: true,
|
||||
|
||||
// Enables automatic instrumentation of Vercel Cron Monitors. (Does not yet work with App Router route handlers.)
|
||||
// See the following for more information:
|
||||
// https://docs.sentry.io/product/crons/
|
||||
// https://vercel.com/docs/cron-jobs
|
||||
automaticVercelMonitors: true,
|
||||
})
|
||||
|
||||
5848
package-lock.json
generated
5848
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -31,6 +31,7 @@
|
||||
"@radix-ui/react-select": "^2.1.2",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"@radix-ui/react-toast": "^1.1.5",
|
||||
"@sentry/nextjs": "^9.15.0",
|
||||
"@stripe/react-stripe-js": "^2.7.1",
|
||||
"@stripe/stripe-js": "^3.4.1",
|
||||
"@t3-oss/env-nextjs": "^0.10.1",
|
||||
|
||||
@@ -56,45 +56,46 @@ const paymentMethodOptions = [
|
||||
{ label: 'EVMs', icon: EvmIcon, value: 'evm' },
|
||||
] as const
|
||||
|
||||
const schema = z
|
||||
.object({
|
||||
amount: z.coerce.number(),
|
||||
paymentMethod: z.enum(['card', 'btc', 'xmr', 'ltc', 'evm']),
|
||||
term: z.enum(['monthly', 'annually']),
|
||||
taxDeductible: z.enum(['yes', 'no']),
|
||||
recurring: z.enum(['yes', 'no']),
|
||||
givePointsBack: z.enum(['yes', 'no']),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
if (data.term === 'monthly' && data.amount < MONTHLY_MEMBERSHIP_MIN_PRICE_USD) {
|
||||
ctx.addIssue({
|
||||
path: ['amount'],
|
||||
code: 'custom',
|
||||
message: `Min. amount is $${MONTHLY_MEMBERSHIP_MIN_PRICE_USD}.`,
|
||||
})
|
||||
}
|
||||
|
||||
if (data.term === 'annually' && data.amount < ANNUALLY_MEMBERSHIP_MIN_PRICE_USD) {
|
||||
ctx.addIssue({
|
||||
path: ['amount'],
|
||||
code: 'custom',
|
||||
message: `Min. amount is $${ANNUALLY_MEMBERSHIP_MIN_PRICE_USD}.`,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
type FormInputs = z.infer<typeof schema>
|
||||
|
||||
function MembershipPage({ fund: fundSlug, project }: Props) {
|
||||
const session = useSession()
|
||||
const router = useRouter()
|
||||
|
||||
const schema = z
|
||||
.object({
|
||||
amount: z.coerce.number(),
|
||||
paymentMethod: z.enum(['card', 'btc', 'xmr', 'ltc', 'evm']),
|
||||
term: z.enum(['monthly', 'annually']),
|
||||
taxDeductible: z.enum(['yes', 'no']),
|
||||
recurring: z.enum(['yes', 'no']),
|
||||
givePointsBack: z.enum(['yes', 'no']),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
if (data.term === 'monthly' && data.amount < MONTHLY_MEMBERSHIP_MIN_PRICE_USD) {
|
||||
ctx.addIssue({
|
||||
path: ['amount'],
|
||||
code: 'custom',
|
||||
message: `Min. amount is $${MONTHLY_MEMBERSHIP_MIN_PRICE_USD}.`,
|
||||
})
|
||||
}
|
||||
|
||||
if (data.term === 'annually' && data.amount < ANNUALLY_MEMBERSHIP_MIN_PRICE_USD) {
|
||||
ctx.addIssue({
|
||||
path: ['amount'],
|
||||
code: 'custom',
|
||||
message: `Min. amount is $${ANNUALLY_MEMBERSHIP_MIN_PRICE_USD}.`,
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
type FormInputs = z.infer<typeof schema>
|
||||
|
||||
const { toast } = useToast()
|
||||
|
||||
const form = useForm<FormInputs>({
|
||||
resolver: zodResolver(schema),
|
||||
defaultValues: {
|
||||
amount: 10,
|
||||
paymentMethod: 'xmr',
|
||||
term: 'monthly',
|
||||
taxDeductible: 'no',
|
||||
recurring: 'no',
|
||||
@@ -228,7 +229,7 @@ function MembershipPage({ fund: fundSlug, project }: Props) {
|
||||
<FormMessage />
|
||||
|
||||
{!form.formState.errors.amount?.message && (
|
||||
<p className="text-xs hidden sm:block"> </p>
|
||||
<p className="text-[0.8rem] font-medium text-teal-500"> </p>
|
||||
)}
|
||||
</FormItem>
|
||||
)}
|
||||
@@ -252,11 +253,15 @@ function MembershipPage({ fund: fundSlug, project }: Props) {
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
{annualTermSavePerc > 0 && (
|
||||
<span className="font-semibold text-teal-500 text-xs">
|
||||
Save {annualTermSavePerc}% with <strong>annual</strong>
|
||||
</span>
|
||||
)}
|
||||
<span className="text-[0.8rem] font-medium text-teal-500">
|
||||
{annualTermSavePerc > 0 ? (
|
||||
<>
|
||||
Save {annualTermSavePerc}% with <strong>annual</strong>
|
||||
</>
|
||||
) : (
|
||||
<> </>
|
||||
)}
|
||||
</span>
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
17
sentry.edge.config.ts
Normal file
17
sentry.edge.config.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
// This file configures the initialization of Sentry for edge features (middleware, edge routes, and so on).
|
||||
// The config you add here will be used whenever one of the edge features is loaded.
|
||||
// Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally.
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
|
||||
import * as Sentry from '@sentry/nextjs'
|
||||
|
||||
if (process.env.NODE_ENV === 'production')
|
||||
Sentry.init({
|
||||
dsn: 'https://029796cccc72916ee470432a28927103@o4509249375436800.ingest.us.sentry.io/4509249381400576',
|
||||
|
||||
// Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
|
||||
tracesSampleRate: 1,
|
||||
|
||||
// Setting this option to true will print useful information to the console while you're setting up Sentry.
|
||||
debug: false,
|
||||
})
|
||||
16
sentry.server.config.ts
Normal file
16
sentry.server.config.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
// This file configures the initialization of Sentry on the server.
|
||||
// The config you add here will be used whenever the server handles a request.
|
||||
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
|
||||
import * as Sentry from '@sentry/nextjs'
|
||||
|
||||
if (process.env.NODE_ENV === 'production')
|
||||
Sentry.init({
|
||||
dsn: 'https://029796cccc72916ee470432a28927103@o4509249375436800.ingest.us.sentry.io/4509249381400576',
|
||||
|
||||
// Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control.
|
||||
tracesSampleRate: 1,
|
||||
|
||||
// Setting this option to true will print useful information to the console while you're setting up Sentry.
|
||||
debug: false,
|
||||
})
|
||||
@@ -9,13 +9,15 @@
|
||||
"strict": true,
|
||||
"strictNullChecks": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"noEmit": false,
|
||||
"outDir": "dist",
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
"incremental": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "auth.d.ts", "**/*.ts", "**/*.tsx", "env.mjs"],
|
||||
"exclude": ["node_modules"]
|
||||
|
||||
Reference in New Issue
Block a user