diff --git a/.example.env b/.example.env
index e0553bc825..aa4a6aaff2 100644
--- a/.example.env
+++ b/.example.env
@@ -62,6 +62,3 @@ DB_PROJECTS_WEBHOOK_URL=
# Discord Webhook URL for /contact page.
CONTACT_WEBHOOK_URL=
-# Github access token (for project archival requests)
-GITHUB_ACCESS_TOKEN=
-
diff --git a/src/components/Project/ArchiveProject.tsx b/src/components/Project/ArchiveProject.tsx
index aa1964dd5e..7c279b6104 100644
--- a/src/components/Project/ArchiveProject.tsx
+++ b/src/components/Project/ArchiveProject.tsx
@@ -1,6 +1,5 @@
import { t, Trans } from '@lingui/macro'
import { Button, Statistic } from 'antd'
-import axios from 'axios'
import { Callout } from 'components/Callout/Callout'
import { PV_V1, PV_V2 } from 'constants/pv'
import { ProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext'
@@ -65,17 +64,6 @@ export function ArchiveProject({
return emitErrorNotification(t`Failed to update project metadata`)
}
- // Create github issue when archive is requested
- // https://docs.github.com/en/rest/reference/issues#create-an-issue
- // Do this first, in case the user closes the page before the on-chain tx completes
- axios.post(`/api/github/archive-project`, {
- archived,
- projectId,
- projectMetadata,
- handle,
- pv,
- })
-
const txSuccessful = await storeCidTx(
{ cid: uploadedMetadata.Hash },
{
@@ -87,8 +75,9 @@ export function ArchiveProject({
},
)
if (!txSuccessful) {
- emitErrorNotification(t`Transaction unsuccessful`)
+ emitErrorNotification(t`Failed to update project metadata`)
setIsLoadingArchive(false)
+ return
}
}
diff --git a/src/components/v2v3/V2V3Project/ProjectDashboard/components/ProjectHeader/ProjectHeader.tsx b/src/components/v2v3/V2V3Project/ProjectDashboard/components/ProjectHeader/ProjectHeader.tsx
index eaebd7d238..10eba86717 100644
--- a/src/components/v2v3/V2V3Project/ProjectDashboard/components/ProjectHeader/ProjectHeader.tsx
+++ b/src/components/v2v3/V2V3Project/ProjectDashboard/components/ProjectHeader/ProjectHeader.tsx
@@ -1,6 +1,7 @@
import { Cog6ToothIcon } from '@heroicons/react/24/outline'
import { Trans } from '@lingui/macro'
import { Button, Divider } from 'antd'
+import { Badge } from 'components/Badge'
import { DomainBadge } from 'components/DomainBadge'
import EthereumAddress from 'components/EthereumAddress'
import { GnosisSafeBadge } from 'components/Project/ProjectHeader/GnosisSafeBadge'
@@ -23,8 +24,16 @@ import { Subtitle } from './components/Subtitle'
import ToolsDrawerButton from './components/ToolsDrawerButton'
export const ProjectHeader = ({ className }: { className?: string }) => {
- const { title, subtitle, domain, projectId, handle, owner, gnosisSafe } =
- useProjectHeader()
+ const {
+ title,
+ subtitle,
+ domain,
+ projectId,
+ handle,
+ owner,
+ gnosisSafe,
+ archived,
+ } = useProjectHeader()
const isMobile = useMobile()
const canReconfigure = useV2V3WalletHasPermission(
V2V3OperatorPermission.RECONFIGURE,
@@ -72,7 +81,9 @@ export const ProjectHeader = ({ className }: { className?: string }) => {
-
+
+ {archived ? Archived : null}
+
{subtitle &&
diff --git a/src/components/v2v3/V2V3Project/ProjectDashboard/components/ProjectHeader/components/ProjectHeaderStats.test.tsx b/src/components/v2v3/V2V3Project/ProjectDashboard/components/ProjectHeader/components/ProjectHeaderStats.test.tsx
index 074b1e1635..a50d263526 100644
--- a/src/components/v2v3/V2V3Project/ProjectDashboard/components/ProjectHeader/components/ProjectHeaderStats.test.tsx
+++ b/src/components/v2v3/V2V3Project/ProjectDashboard/components/ProjectHeader/components/ProjectHeaderStats.test.tsx
@@ -36,6 +36,7 @@ const MOCK_PROJECT_HEADER_DATA: ProjectHeaderData = {
totalVolume: BigNumber.from(420),
last7DaysPercent: 69,
gnosisSafe: undefined,
+ archived: false,
}
describe('ProjectHeaderStats', () => {
diff --git a/src/components/v2v3/V2V3Project/ProjectDashboard/hooks/useProjectHeader.ts b/src/components/v2v3/V2V3Project/ProjectDashboard/hooks/useProjectHeader.ts
index 4faa19d2d9..e60b2e8452 100644
--- a/src/components/v2v3/V2V3Project/ProjectDashboard/hooks/useProjectHeader.ts
+++ b/src/components/v2v3/V2V3Project/ProjectDashboard/hooks/useProjectHeader.ts
@@ -18,6 +18,7 @@ export interface ProjectHeaderData {
totalVolume: BigNumber | undefined
last7DaysPercent: number
gnosisSafe: GnosisSafe | undefined | null
+ archived: boolean | undefined
}
export const useProjectHeader = (): ProjectHeaderData => {
@@ -48,5 +49,6 @@ export const useProjectHeader = (): ProjectHeaderData => {
totalVolume,
last7DaysPercent,
gnosisSafe,
+ archived: projectMetadata?.archived,
}
}
diff --git a/src/hooks/v2v3/usePayProjectDisabled.ts b/src/hooks/v2v3/usePayProjectDisabled.ts
index 7e208af91d..7a7fe1fe6d 100644
--- a/src/hooks/v2v3/usePayProjectDisabled.ts
+++ b/src/hooks/v2v3/usePayProjectDisabled.ts
@@ -1,6 +1,7 @@
import { t } from '@lingui/macro'
import { useProjectContext } from 'components/v2v3/V2V3Project/ProjectDashboard/hooks/useProjectContext'
import { useProjectIsOFACListed } from 'components/v2v3/V2V3Project/ProjectDashboard/hooks/useProjectIsOFACListed'
+import { useProjectMetadataContext } from 'contexts/shared/ProjectMetadataContext'
import { useV2V3BlockedProject } from './useBlockedProject'
export enum PayDisabledReason {
@@ -23,6 +24,7 @@ export function usePayProjectDisabled(): {
reason: PayDisabledReason | undefined
message: string | undefined
} {
+ const { projectMetadata } = useProjectMetadataContext()
const { fundingCycleMetadata, loading } = useProjectContext()
const isBlockedProject = useV2V3BlockedProject()
const { isAddressListedInOFAC, isLoading: isOFACLoading } =
@@ -61,6 +63,14 @@ export function usePayProjectDisabled(): {
}
}
+ if (projectMetadata?.archived) {
+ return {
+ ...disabled,
+ reason: PayDisabledReason.BLOCKED,
+ message: t`This project has been archived and can't be paid.`,
+ }
+ }
+
return {
payDisabled: false,
loading: false,
diff --git a/src/lib/api/supabase/projects/logger.ts b/src/lib/api/supabase/projects/logger.ts
index 0e3f6e3f38..7dcd9ca293 100644
--- a/src/lib/api/supabase/projects/logger.ts
+++ b/src/lib/api/supabase/projects/logger.ts
@@ -35,7 +35,7 @@ export async function dbpLog(
// log the error to the console
if (type === 'alert') {
- logger.error({ data: { type, message: DBP_ALERTS[opts.alert] } })
+ logger.error({ data: { type, message: DBP_ALERTS[opts.alert], body } })
} else {
logger.info({ data: { type, message: DBP_NOTIFS[opts.notif] } })
}
diff --git a/src/locales/messages.pot b/src/locales/messages.pot
index d341278835..bf6cbd5707 100644
--- a/src/locales/messages.pot
+++ b/src/locales/messages.pot
@@ -200,6 +200,9 @@ msgstr ""
msgid "Juicebox loading animation"
msgstr ""
+msgid "This project has been archived and can't be paid."
+msgstr ""
+
msgid "Payments to this project paused"
msgstr ""
@@ -4691,9 +4694,6 @@ msgstr ""
msgid "Potential risks"
msgstr ""
-msgid "Transaction unsuccessful"
-msgstr ""
-
msgid "All-in-one crowdfunding with powerful treasury management and redemptions."
msgstr ""
diff --git a/src/pages/api/github/archive-project.ts b/src/pages/api/github/archive-project.ts
deleted file mode 100644
index 1baddf4fe6..0000000000
--- a/src/pages/api/github/archive-project.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import axios from 'axios'
-import { AnyProjectMetadata } from 'models/projectMetadata'
-import { NextApiRequest, NextApiResponse } from 'next'
-
-import { readNetwork } from 'constants/networks'
-import { PV_V2 } from 'constants/pv'
-import { PV } from 'models/pv'
-
-interface ArchiveProjectNextApiRequest extends NextApiRequest {
- body: {
- archived: boolean
- projectId: number | undefined
- metadata: AnyProjectMetadata | undefined
- handle: string | undefined
- pv: PV
- }
-}
-
-const handler = async (
- req: ArchiveProjectNextApiRequest,
- res: NextApiResponse,
-) => {
- const githubToken = process.env.GITHUB_ACCESS_TOKEN
- if (!githubToken) {
- res.status(500).json({
- error: 'GITHUB_ACCESS_TOKEN is not set',
- })
- return
- }
-
- try {
- const { archived, projectId, metadata, handle, pv } = req.body
-
- if (!projectId || !metadata || !handle) {
- throw new Error()
- }
-
- const headers = {
- Authorization: `Bearer ${githubToken}`,
- }
-
- const title = `[${archived ? 'ARCHIVE' : 'UNARCHIVE'}] Project: "${
- metadata.name
- }"`
-
- const body = `
- Chain: ${readNetwork.name}
- Project ID: ${projectId}
- ${handle ? `Handle: ${handle}` : ''}
- `
-
- const labels = [
- 'archive request',
- pv === PV_V2 ? 'V2' : 'V1',
- 'bot',
- readNetwork.name === 'goerli' ? 'site:goerli' : undefined,
- readNetwork.name === 'mainnet' ? 'site:mainnet' : undefined,
- ].filter(Boolean)
-
- const data = {
- title,
- body,
- labels,
- }
-
- const response = await axios.post(
- 'https://api.github.com/repos/jbx-protocol/juice-interface/issues',
- data,
- {
- headers,
- },
- )
-
- res.status(200).json({
- response,
- })
- } catch (error) {
- return res.status(500)
- }
-}
-
-export default handler
diff --git a/src/pages/api/ipfs/[cid].ts b/src/pages/api/ipfs/[cid].ts
index 928c7bebcf..1f54baf3b0 100644
--- a/src/pages/api/ipfs/[cid].ts
+++ b/src/pages/api/ipfs/[cid].ts
@@ -8,6 +8,9 @@ export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
+ if (!req) {
+ res.status(500).end()
+ }
if (req.method !== 'GET') {
return res.status(405).end()
}
diff --git a/src/pages/api/juicebox/pv/[pv]/project/[projectId]/refreshMetadata.ts b/src/pages/api/juicebox/pv/[pv]/project/[projectId]/refreshMetadata.ts
new file mode 100644
index 0000000000..76cd15edef
--- /dev/null
+++ b/src/pages/api/juicebox/pv/[pv]/project/[projectId]/refreshMetadata.ts
@@ -0,0 +1,33 @@
+import { createServerSupabaseClient } from '@supabase/auth-helpers-nextjs'
+import { NextApiRequest, NextApiResponse } from 'next'
+import { Database } from 'types/database.types'
+import { getProjectMetadata } from 'utils/server/metadata'
+
+/**
+ * Force refresh a given project's metadata in the database.
+ */
+const handler = async (req: NextApiRequest, res: NextApiResponse) => {
+ if (req.method !== 'PUT') {
+ return res.status(405).end()
+ }
+
+ try {
+ const { projectId, pv } = req.query
+ if (!projectId || !pv) {
+ return res.status(400).json({ error: 'projectId is required' })
+ }
+
+ const supabase = createServerSupabaseClient({ req, res })
+
+ const metadata = await getProjectMetadata(projectId as string)
+ await supabase
+ .from('projects')
+ .update({ archived: metadata?.archived }) // TODO add more
+ .eq('id', projectId)
+
+ return res.status(204).end()
+ } catch (error) {
+ return res.status(500).end()
+ }
+}
+export default handler
diff --git a/src/pages/api/nextjs/revalidate-project.ts b/src/pages/api/nextjs/revalidate-project.ts
index 46781989d6..c496c65dc2 100644
--- a/src/pages/api/nextjs/revalidate-project.ts
+++ b/src/pages/api/nextjs/revalidate-project.ts
@@ -43,7 +43,7 @@ export default async function handler(
// Update database projects whenever a project needs revalidating. However, database will only get updated if the new project data is already available in the subgraph, which can sometimes take a couple minutes
await axios
- .get('/api/projects/update')
+ .get(`${process.env.NEXT_PUBLIC_BASE_URL}api/projects/update`)
// can throw error when env isnt set correctly
.catch(err => console.error('Database projects update failed', err))
diff --git a/src/pages/api/projects/update-retry-ipfs.ts b/src/pages/api/projects/update-retry-ipfs.ts
deleted file mode 100644
index 995833c541..0000000000
--- a/src/pages/api/projects/update-retry-ipfs.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { updateDBProjects } from 'lib/api/supabase/projects'
-import { NextApiHandler } from 'next'
-
-// Synchronizes the Sepana engine with the latest Juicebox Subgraph/IPFS data
-const handler: NextApiHandler = async (_, res) => {
- await updateDBProjects(res, true)
-}
-
-export default handler