Skip to content

Commit

Permalink
Merge pull request #2 from iggy-social/spheron-storage
Browse files Browse the repository at this point in the history
Changing the image upload provider
  • Loading branch information
tempe-techie authored Nov 27, 2023
2 parents ecc7001 + 6c166f8 commit affa07c
Show file tree
Hide file tree
Showing 24 changed files with 1,016 additions and 261 deletions.
9 changes: 6 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
TENOR_KEY=
WEB3_STORAGE_KEY=
RPC_CUSTOM=
FILE_UPLOAD_SERVICE=
LINK_PREVIEW_SERVICE=
RPC_CUSTOM=
SPHERON_BUCKET_NAME=
SPHERON_STORAGE_TOKEN=
TENOR_KEY=
1 change: 0 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ jobs:
runs-on: ubuntu-latest
env: # Environment variables from either organization or repository settings page (for GitHub Actions).
TENOR_KEY: ${{ secrets.TENOR_KEY }}
WEB3_STORAGE_KEY: ${{ secrets.WEB3_STORAGE_KEY }}
steps:
- name: Checkout 🛎️
uses: actions/[email protected]
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ dist

# Local Netlify folder
.netlify
.vercel
24 changes: 20 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,22 @@ Make sure to use the the `npm run generate` command instead of `npm run build` f

If you want to use optional features such as GIFs and image upload, make sure to enter proper environment variables (see `.env.example`).

Make sure to also select the proper serverless functions services in your environment variables, for example:

```bash
FILE_UPLOAD_SERVICE=netlify
LINK_PREVIEW_SERVICE=netlify
```

You can also set these in the Nuxt config file (`nuxt.config.ts`).

### 4everland

[4everland](https://4everland.org/) is a decentralized hosting provider which stores your website on IPFS.

If you have your code on GitHub, the `build.yml` script will build your app via GitHub Actions and create a `build` branch.

Make sure you add all the necessary env vars (tenor, web3 storage etc.) to the organization variables for actions on GitHub.
Make sure you add all the necessary env vars (tenor etc.) to the organization variables for actions on GitHub.

Also make sure you have Workflow permissions on the organization level on GitHub set to read & write.

Expand All @@ -47,11 +56,18 @@ If you want to have GIF search implemented, create your own Tenor API Key on Goo

Then enter the key in environment variables (`TENOR_KEY`).

## Image upload (Web3 Storage)
## Image upload (Spheron/IPFS)

To support image uploads on IPFS please create an API key on Web3 Storage: https://web3.storage/
To support image uploads on IPFS please create a key/token on Spheron Storage: https://app.spheron.network/#/storage

Then add this key (and your bucket ID/name) to your environment variables:

```bash
SPHERON_BUCKET_NAME=
SPHERON_STORAGE_TOKEN=
```

Then enter the key in environment variables (`WEB3_STORAGE_KEY`).
Image uploads via Spheron work only if you have Netlify/Vercel background functions enabled (see `netlify/functions/imageUploader.js`).

## Customize

Expand Down
3 changes: 3 additions & 0 deletions api/_readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Vercel serverless function

This folder contains serverless functions for Vercel.
25 changes: 25 additions & 0 deletions api/imageUploader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const { SpheronClient, ProtocolEnum } = require("@spheron/storage");

export default async function handler(request, response) {
try {
const bucketName = process.env.SPHERON_BUCKET_NAME; // enter bucket name in environment variables
const token = process.env.SPHERON_STORAGE_TOKEN; // add spheron storage token in environment variables

const protocol = ProtocolEnum.IPFS;

const client = new SpheronClient({ token });

const { uploadToken } = await client.createSingleUploadToken({
name: bucketName,
protocol,
});

return response.status(200).json({
data: uploadToken
});

} catch (error) {
console.error(error);
next(error);
}
}
199 changes: 199 additions & 0 deletions api/linkPreviews.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
export default async function handler(request, response) {
const ethers = require('ethers');

const metascraper = require('metascraper')([
require('metascraper-description')(),
require('metascraper-image')(),
require('metascraper-title')()
])

const url = request.query.url;

// link previews for specific sites
if (url.startsWith("https://sparklesnft.com/item/flare/")) {
// SPARKLES ITEM
const cleanUrl = url.split("?")[0];
const addrTokenId = cleanUrl.split("https://sparklesnft.com/item/flare/")[1].replace("/", "");
const addr = addrTokenId.split("_")[0];
const tokenId = addrTokenId.split("_")[1];

const provider = new ethers.providers.JsonRpcProvider("https://flare-api.flare.network/ext/C/rpc");

const nftInterface = new ethers.utils.Interface([
"function uri(uint256 _tokenId) external view returns (string memory)",
"function tokenURI(uint256 _tokenId) external view returns (string memory)"
]);

const nftContract = new ethers.Contract(addr, nftInterface, provider);

let nftMetadataUri;

try {
// erc-1155
nftMetadataUri = await nftContract.uri(tokenId);
} catch (error) {
// erc-721
nftMetadataUri = await nftContract.tokenURI(tokenId);
}

let json;

if (
!nftMetadataUri.startsWith("https://") &&
!nftMetadataUri.startsWith("http://") &&
!nftMetadataUri.startsWith("ipfs://")
) {
const result = atob(nftMetadataUri.substring(29));
json = JSON.parse(result);
} else if (nftMetadataUri.startsWith("ipfs://")) {
// ipfs://QmTLJDoCTihqD3AkNkMMQFXLMQFZkdqLFR3Mdcg14Ln7bL/7.json
// https://bafkreiecjcejcg6h5wh2g3wcacy2lnod2mfrvimlr53ikvlsh53mw2zdty.ipfs.w3s.link/
// https://QmTLJDoCTihqD3AkNkMMQFXLMQFZkdqLFR3Mdcg14Ln7bL.ipfs.dweb.link/7.json
// https://bafybeickgncmppcbrhg4cbzw3nxqajxoe2vj7ixmmcshddkyn5itv7ang4.ipfs.dweb.link/7.json

json = {
"name": "Check this NFT on Sparkles",
"description": "Sparkles NFT Marketplace",
"image": "https://bafkreifyx3seviqnnhpcuge72lr7la3yfrvucr3n5aoqijzjul2fyzh64i.ipfs.w3s.link"
}
} else {
const res = await fetch(nftMetadataUri);
json = await res.json();
}

const finalMetadata = {
"url": url,
"title": json["name"],
"description": json["description"],
"image": {
url: json["image"]
}
};

return response.status(200).json({
data: finalMetadata
});
} else if (url.startsWith("https://opensea.io/assets/base/")) {
// OPENSEA ITEM
const cleanUrl = url.split("?")[0];
const addrTokenId = cleanUrl.split("opensea.io/assets/base/")[1];
const addr = addrTokenId.split("/")[0];
const tokenId = addrTokenId.split("/")[1].replace("/", "");

const provider = new ethers.providers.JsonRpcProvider("https://mainnet.base.org");

const nftInterface = new ethers.utils.Interface([
"function uri(uint256 _tokenId) external view returns (string memory)",
"function tokenURI(uint256 _tokenId) external view returns (string memory)"
]);

const nftContract = new ethers.Contract(addr, nftInterface, provider);

let nftMetadataUri;

try {
// erc-1155
nftMetadataUri = await nftContract.uri(tokenId);
} catch (error) {
// erc-721
nftMetadataUri = await nftContract.tokenURI(tokenId);
}

let json;

if (
!nftMetadataUri.startsWith("https://") &&
!nftMetadataUri.startsWith("http://") &&
!nftMetadataUri.startsWith("ipfs://")
) {
const result = atob(nftMetadataUri.substring(29));
json = JSON.parse(result);
} else if (nftMetadataUri.startsWith("ipfs://")) {
// ipfs://QmTLJDoCTihqD3AkNkMMQFXLMQFZkdqLFR3Mdcg14Ln7bL/7.json
// https://bafkreiecjcejcg6h5wh2g3wcacy2lnod2mfrvimlr53ikvlsh53mw2zdty.ipfs.w3s.link/
// https://QmTLJDoCTihqD3AkNkMMQFXLMQFZkdqLFR3Mdcg14Ln7bL.ipfs.dweb.link/7.json
// https://bafybeickgncmppcbrhg4cbzw3nxqajxoe2vj7ixmmcshddkyn5itv7ang4.ipfs.dweb.link/7.json

json = {
"name": "Check this NFT on OpenSea",
"description": "OpenSea is the world's first and largest web3 marketplace for NFTs and crypto collectibles. Browse, create, buy, sell, and auction NFTs using OpenSea today.",
"image": "https://static.opensea.io/og-images/Metadata-Image.png"
}
} else {
const res = await fetch(nftMetadataUri);
json = await res.json();
}

const finalMetadata = {
"url": url,
"title": json["name"],
"description": json["description"],
"image": {
url: json["image"]
}
};

return response.status(200).json({
data: finalMetadata
});
} else if (url.startsWith("https://twitter.com") || url.startsWith("https://x.com")) {
// TWITTER
const finalMetadata = {
"url": url,
"title": "Twitter / X.com",
"description": "Elon's land, Elon's rules. Enter at your own risk.",
"image": {
url: "https://www.newswire.lk/wp-content/uploads/2022/12/elon-musk-twitter.jpg"
}
};

return response.status(200).json({
data: finalMetadata
});
}

// link previews for all other sites
try {
const res = await fetch(url);
const html = await res.text();

const metadata = await metascraper({ html, url });

const finalMetadata = {
"url": url,
"title": metadata.title,
"description": metadata.description,
"image": {
url: metadata.image
}
};

if (finalMetadata?.title) {
if (url.startsWith("https://opensea.io") && (finalMetadata.title == "Access denied" || finalMetadata.title.startsWith("Just a moment"))) {
finalMetadata.title = "OpenSea";
finalMetadata.description = "OpenSea is the world's first and largest web3 marketplace for NFTs and crypto collectibles. Browse, create, buy, sell, and auction NFTs using OpenSea today.";
finalMetadata.image.url = "https://static.opensea.io/og-images/Metadata-Image.png";
} else if (url.startsWith("https://dune.com") && (finalMetadata.title.startsWith("Attention Required") || finalMetadata.title.startsWith("Just a moment"))) {
finalMetadata.title = "Dune";
finalMetadata.description = "Blockchain ecosystem analytics by and for the community. Explore and share data from Ethereum, Polygon, Arbitrum, Optimism, and others for free.";
finalMetadata.image.url = "https://dune.com/assets/poster-1440w.png";
} else if (finalMetadata.title.startsWith("Just a moment...")) {
return response.status(500).json({
error: "Data not fetched yet."
});
}
}

return response.status(200).json({
data: finalMetadata
});

} catch (error) {
console.error('Error fetching link preview metadata:', error);

return response.status(500).json({
error: error
});
}

};
70 changes: 70 additions & 0 deletions components/VerifyAccountOwnership.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<template>
<!-- Verify Account Ownership modal -->
<div class="modal fade" id="verifyAccountModal" tabindex="-1" aria-labelledby="verifyAccountModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Verify Account Ownership</h5>
<button id="closeVerifyAccountModal" type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close">
<span aria-hidden="true"></span>
</button>
</div>
<div class="modal-body p-3 mb-3">

<p>
Verify that you really own this account/wallet. This will allow you to use chat, change profile image, and set some other settings.
</p>

<button class="btn btn-primary" @click="connectToOrbis">
Verify your account
</button>

</div>
</div>
</div>
</div>
<!-- END Connect Wallet modal -->
</template>

<script>
import { useUserStore } from '~/store/user';
import { useToast } from "vue-toastification/dist/index.mjs";
export default {
name: "VerifyAccountOwnership",
methods: {
async connectToOrbis() {
let provider = this.$getFallbackProvider(this.$config.supportedChainId);
let res = await this.$orbis.connect_v2({
provider: provider.provider,
lit: false
});
/** Check if connection is successful or not */
if(res.status == 200) {
this.userStore.setIsConnectedToOrbis(true);
if (this.$orbis.session) {
this.userStore.setDid(this.$orbis.session.did._id);
this.userStore.setDidParent(this.$orbis.session.did._parentId);
}
document.getElementById('closeVerifyAccountModal').click();
} else {
console.log("Error verifying account: ", res);
this.toast(res.result, {type: "error"});
}
},
},
setup() {
const userStore = useUserStore();
const toast = useToast();
return { userStore, toast };
},
}
</script>
Loading

0 comments on commit affa07c

Please sign in to comment.