diff --git a/404.html b/404.html new file mode 100644 index 00000000..574d45c1 --- /dev/null +++ b/404.html @@ -0,0 +1,33 @@ + + +
+ + + + + +In this section, you will learn everything you need to know before you start building on Web3 with Apillon.
The main topics you can explore are:
Apillon is a Web3 development platform for developers and businesses building on Web3. By gathering unified API endpoints, it delivers the functionalities of Polkadot parachains adapted to the standard development process and user-preferred technology stack.
It gathers a unique set of tools, ample SDKs, and comprehensive documentation to fully support users in building their Web3 products from day one.
With Apillon, developers can create a complex and fully-functional Web3-based product in just days, harnessing the services provided by different parachains but without the need to employ them individually and from scratch or to spend years mastering the technical knowledge.
This way, they can focus more on the functionalities of their Web3 product than the underlying blockchain complexity and drastically shorten its go-to-market timeline.
The dawn of open-source and decentralized technologies has inspired the creation of systems that give individuals more power to exchange assets and open up new business opportunities while reducing their reliance on centralized authorities. However, even though the pace of technological progress has been outstanding, the adoption of decentralized solutions still awaits implementation in the real economy on a large scale.
Polkadot, one of the leading players in the Web3 ecosystem, is set to transform the way businesses, individuals, and organizations manage assets and data. Still, several factors hinder the broader adoption of Web3 solutions:
Apillon's unified points of access deliver the functionalities of Polkadot parachains to developers simply through APIs, so they can create a working Web3-based product from day one while mitigating or avoiding the issues above.
While each parachain is best at solving its own isolated case, Apillon is all about connecting and curating different parachains, protocols, or pallets into the right context that can be easily adopted by developers to build tangible products in Web3.
Simplification further applies to pricing, as the platform takes care of all the underlying payment requirements with a unified payment system. Payment plans in DOT or fiat (EUR, USD) deliver price stability and predictability, ideal for enterprises and frequent users.
Building with Apillon does not require prior knowledge of blockchain technology and surpasses the hassle of lengthy testing and auditing. A streamlined product development substantially reduces the go-to-market timeline and resources spent in the process.
Apillon brings the power of distributed technologies to web developers and opens the floodgate to their widespread adoption. It democratizes the usability of advanced technologies and helps Web3 services reach wider and deeper.
Unpredictable pricing of Web3 services hinders adoption as users cannot foresee the costs generated in the process of using them. The Apillon platform delivers various Web3 services at stable pricing and allows users to devote more time and energy to developing new products.
The Web3 space delivers outstanding possibilities for building advanced decentralized products. However, the projects providing Web3 services have mostly focused on perfecting the technology offering and less on how end-users interact with it and adopt it.
One of the main challenges users need to overcome in Web3 services adoption is token/asset acquisition and management. This could seem like a minor issue when using a single service but could grow into an overwhelming toil when utilizing multiple at once.
"Apillon is a Web3 development platform that gathers and delivers complex blockchain solutions and protocols through a simplified and unified API endpoint, allowing developers to build Web3 projects quickly, effectively and with predictable pricing." Apillon Tokenomics Whitepaper
To deliver as smooth a user experience as possible, the Apillon platform performs automatic payments of underlying services in their native tokens and enables users to pay for all in a single transaction.
Each successful startup goes through typical development phases. First, after the initial idea, they build a prototype and MVP to create traction. Once they perfect their offering in the product-market fit phase, they work towards boosting growth in the scaling phase.
In supporting Web3 builders through their growth process, Apillon delivers a full-stack solution that allows both startups and established companies to develop and scale their products using Web3 building services.
Too often, as they work on perfecting the product-market fit, initial-stage businesses are not focused much on the costs that come with using different services. However, in the following phase, scaling the business model becomes a much more pragmatic issue, as it requires a lot of mid- to long-term planning and continuous cost optimization.
And this can only be achieved by utilizing services with reliable, predictable pricing models.
In the Web3 world, the pricing of most protocols, parachains, and services is typically based around a volatile token. This is directly correlated with project tokenomics, as they aim to boost token value with different token utilities, some more successfully, some less. Moreover, token valuation is heavily subject to market sentiment and community backing.
Charging for Web3 services in volatile tokens leads to unpredictable pricing models, which prevents their users from scaling successfully and reining their spending within rational range.
The unpredictability of service pricing is a huge issue in the current stage of the Web3 space, regardless of (or because of) ever-changing market sentiment. With the implementation of stable service pricing, Apillon provides the platform users with tools to build stellar Web3 products while allowing them to forecast their financial statements and cash flows for stable growth.
Paying for services with a highly volatile asset is both risky and cumbersome. It is mainly because, in Web3, highly volatile assets represent more an investment vehicle than a value transfer instrument.
The Apillon platform is designed to accept payments in either fiat money, stablecoins, or even volatile assets. As it cannot impact the utility of supported parachains' native tokens, the platform sustains a pricing model that resists their volatility and provides users with stable monthly pricing. This way, they can adequately plan the resources spent on Web3 projects.
While Apillon’s pricing model can buffer normal average asset volatility of supported services, it is not designed to withstand cases of dramatic or black swan events on the market.
The Apillon platform and its pricing is built using the following approaches:
The Apillon MVP supports a freemium model to bring the above points into equilibrium. With stably priced services and unified payment, it further helps Apillon's business model to stand the test of the crypto market.
Apillon abstracts the technological complexity behind the Polkadot network and its parachains into a suite of development tools empowering developers to upgrade to Web3 easily.
The platform gathers and delivers Web3 functionalities powered by Polkadot parachains to developers through straightforward API access, and provides support with ample SDKs and complete documentation for each service.
Apillon's adaptability and interoperability allow builders to seamlessly merge use cases into single products offering different distributed services, serving their audiences more comprehensively than they could with specialized products.
Thanks to easy integration into any development framework, the Apillon APIs introduce a new way of Web3 development that significantly simplifies the go-to-market product evolution.
Initially, the Apillon platform will incorporate APIs to the following Polkadot parachains:
In the future, more APIs will be added as new parachains earn their slot on Polkadot, and new Web3 services and features are released on the network.
Easing the integration of the Apillon platform and its APIs, ample SDKs enable faster and more streamlined utilization of Polkadot/Kusama parachains from the first click onwards.
Guiding developers’ way through the individual modules and dapp development process, Apillon documentation helps avoid bugs, mitigate operational issues, and create working products with a lower risk of error and less need for auditing.
With a real-time overview of back-end performance, developers gain valuable insights into the functioning of their Web3 applications and attached services, allowing them to improve on lagging features or tweak the product for better performance.
To help advance the adoption of Web3 products, Apillon's Analytics delivers clear insight into UI and transaction data, allowing developers to understand how their products are used, their best-performing features, and areas that need improvement.
When building on Web3, be it within the Polkadot ecosystem or in general, developers face challenges that still lack proper addressing.
With these obstacles in mind, the process of building a Web3 product from scratch soon becomes a complex, time-consuming, and potentially even risky business.
To overcome them, developers would need to:
Using Apillon, on the other hand, the process of developing a Web3 product is radically different.
Example #1: Building on KILT Protocol*
To build a simple Web3 app utilizing KILT Protocol for user authentication from scratch, a developer needs to do (at least) the following:
Using the Apillon platform, on the other hand, a developer simply calls a function (e. g., createUserWithEmailAndPassword
) from the Apillon SDK and sends the required parameters. The function creates a fully working user DID in the back end.
Example #2: Building on Crust*
To build a simple Web3 app utilizing Crust for file storage from scratch, a developer needs to do (at least) the following:
Using the Apillon platform, on the other hand, a developer simply calls the getStorage()
SDK function and moves the files to a decentralized, pinned service provided by Crust and IPFS.
In both cases, the resources spent on building a functional Web3 application with Apillon are significantly reduced, and the product’s go-to-market trajectory much shorter and streamlined.
*Disclaimer: These examples are technically highly simplified to illustrate the problematic context of building a Web3 product to the general public, whereas in technical reality, the process is much more complex. Examples used do not intend to imply in any way that either KILT or Crust are challenging to use but merely to show that these processes require serious work and introduce friction in cases where developers utilize several parachains to build a single solution.
Polkadot is a “blockchain of blockchains,” allowing many different types of chains to work and interact securely together within the same ecosystem. The network has already contributed great promise and significant progress by encouraging individual sovereignty over personal and business data. Andmore importantly, the integration of diverse parachains into a scalable and flexible network set a new standard for Web3 builders.
Bitcoin - the very first application of blockchain. Soon, the potential to create other decentralized applications grew big. Through the need for improved tools and technological basis, the next-gen blockchain upgrade sprouted in the form of the Ethereum network. A smart-contract-based technology made it possible to exchange value besides just the currency and gave way for developers to build and deploy dapps on top of Ethereum.
Ethereum - using the one-network-serves-all model resulted in limitations like flexibility, scalability, and sovereignty. While building dapps on Ethereum proved beneficial for many industry verticals and demonstrated innovations increase, the mass-scale adoption still fell behind because of the rigid network system and limited scalability of transactions.
The Polkadot network is becoming the forefront of a scalable Web3 universe-building and mass adoption of blockchain – by connecting different blockchains into one unified network. Unlike layer-1 blockchains, layer 0 of Polkadot allows for multiple layer-1 blockchains to be built on top of it. The Polkadot team even created a framework called Substrate, where developers can build blockchains that work independently from one another.
Polkadot’s layer 0, or the so-called relay chain, executes minimal basic actions in terms of network security and makes the communication between chains possible. The blockchains as parachains design their own governance structures, fine-tune their functionalities, and (usually) serve a specific use case purpose.
It’s a win-win situation:
Parachains have more flexibility in building the architecture and therefore contribute better, more specialized services with improved efficiency. Polkadot as an ecosystem offers higher scalability by processing transactions on different chains in parallel and gives way for more innovation due to more specialized parachains.
Polkadot is currently the leading, highest value, and most secure proof-of-stake platform. Nevertheless, every technology’s success can benefit from a reliable support system that can enhance its adoption and bring it to the masses. Each innovation is only as strong as its community of users.
Web3 developers still face considerable challenges when building on Web3. Polkadot offers an excellent foundation, but there’s still enormous amounts of time that need to be invested in the research and development by the builders.
By making Web3 easier to build on, the Apillon platform will assist in the adoption of use-case-specific Polkadot parachains and bring the utility of Web3 to enterprises and end-users that would benefit from (at least partially) blockchain-based processes.
Web3 is a next-generation, decentralized, and democratic version of the web where the way users control and exchange their data is redefined to empower them to partake in the building of the web, take ownership of their data and identity, as well as co-govern the processes. Web3 is built upon an infrastructure that incorporates blockchain technology and decentralized means of data and asset exchange.
Smart contracts are digital automatic agreements that are stored on a blockchain. Secure and trustless programs that execute functions when pre-determined terms and conditions are met, smart contracts deliver transparency, accuracy, and efficiency. At a basic level, smart contracts follow "if/when… then" statements that are written into code.
In a decentralized network, there is no single central authority that controls all data and governs the processes of data and asset exchange. Instead, a decentralized network is controlled by the pre-determined logic that’s written into code on the blockchain. As the code is stored on a decentralized network of nodes around the globe, this delivers an infrastructure that enables peer-to-peer data and asset exchange, which can allow users to regain control over their data and identity.
Nodes create, receive and communicate information that they then store or relay to other nodes. The definition of a node is directly tied to the type of network it participates in – in a blockchain network, blockchain nodes are devices, usually computers, that run the blockchain’s software to validate and broadcast transactions as well as secure the network. Blockchain nodes communicate with each other, and the more nodes there are, the more decentralized the blockchain network is.
A decentralized application (dapp) is built upon a decentralized system for executing its operations. Its backend code runs on a decentralized network, usually a blockchain, instead of a centralized server. This means the operations are inherently immutable, irreversible, and sealed on the decentralized network. The app’s data and files, for example, can be hosted and stored on a decentralized network of nodes – once deployed, the files can’t be taken down or tampered with.
The word ‘trustless’ describes the online exchange of data and assets in Web3 that puts trust into the logic of the code in the backend instead of a single central authority. The core infrastructure of Web3 is controlled by the code in the backend, which eliminates the need to trust third-party service providers or big tech to manage the exchange of data and assets between the users.
Contrary to files, websites, or apps that are stored and hosted on central servers with a single point of access and failure, decentralized or Web3-based assets, websites, or apps can be stored and hosted on a global decentralized network of nodes. This defies single censorship, tampering, or single-point attacks, making them unstoppable.
Parity Technologies is a leading blockchain infrastructure company. Founded by Gavin Wood, Parity Technologies has been building the foundation of Web3 by designing Polkadot and the Substrate blockchain development framework.
Polkadot Network is one of the key actors in Web3. As a sharded blockchain, Polkadot Network enables blockchains with different use cases to securely connect into a unified network and operate seamlessly together at scale. This enables apps and services that are built on Polkadot to securely communicate and form the basis for an interoperable decentralized web.
Polkadot is a sharded blockchain. Connecting several blockchains into a unified network, Polkadot Network allows the separate chains to securely process transactions and exchange data at the same time. Each chain can be optimized for a specific use case while still being able to communicate with the other chains in the network. This unique model is called Polkadot’s heterogeneous sharding model.
The relay chain is Polkadot’s main chain that executes minimal basic actions to ensure network security. It also enables communications between (para)chains.
Parachains are different Polkadot blockchains that connect to the main relay chain. With their own governance structures and different functionalities, parachains serve a specific use case, yet they are able to communicate with each other. The ability of Polkadot’s ecosystem to process transactions on different chains in parallel delivers high scalability.
Founded by Gavin Wood, Web3 Foundation offers support and funds research for technologies and applications in the area of decentralized web software protocols.
Substrate is an open-source and future-proof blockchain framework developed by Parity Tecnologies. It helps developers build blockchains that can work independently from one another while connecting to the main relay chain. Substrate was designed to power the Polkadot Network and a multi-chain, decentralized web.
Phala Network is a Polkadot parachain that provides decentralized computing power and back-end hosting for complex applications while protecting confidentiality and data privacy.
Crust Network is a multi-purpose Polkadot parachain that delivers decentralized storage for massive amounts of data while prioritizing data privacy and user data ownership.
KILT Protocol is a blockchain protocol and Polkadot parachain that provides decentralized digital authentication with self-sovereign, verifiable, revocable, and anonymous credentials.
Web3 project is a website, program, or app that’s built using decentralized technologies. Even standard Web2 projects can be upgraded to the Web3-level by making small changes and incorporating blockchain technology. The Apillon platform makes building Web3 projects simpler and faster.
Web3 services are features that are powered by blockchain technology instead of a centralized service provider. The Apillon platform delivers essential Web3 services for building websites, applications, and projects through simple API calls.
Web3 Authentication is a Web3 service that enables decentralized verification of unique sets of data, identities, and assets. The Apillon platform offers Web3 authentication services powered by KILT Protocol, which delivers secure identity solutions and credentials for enterprises and consumers.
Web3 Storage is a Web3 service that connects to a decentralized network of computer nodes to store the files of a website or app, increasing accessibility and eliminating a single point of access. As the files get stored on a decentralized network of nodes, they become unstoppable. The Apillon platform delivers Web3 Storage service powered by Crust Network.
Web3 Hosting is a Web3 service that connects to a decentralized network of computer nodes to host a website or app, increasing its accessibility and eliminating a single point of access. As the website or app gets hosted on a decentralized network of nodes, it becomes unstoppable. The Apillon platform delivers Web3 Hosting service powered by Crust Network.
Web3 Computing is a Web3 service that offers decentralized cloud computing power by connecting to a peer-to-peer network of computer nodes. This provides privacy, security, and data confidentiality. The Apillon platform delivers Web3 Computing service, powered by Phala Network.
Unified service pricing is a reliable business model that enables the user to predict the cost of services and pay for multiple services in a single transaction. The Apillon platform delivers various Web3 services at stable monthly pricing with DOT and fiat that allows users to devote more time to developing new products as they don’t have to deal with the typically unstable pricing of Web3 services. Read more >
A freemium business model offers basic services free of charge, while a premium is charged for the more advanced services and features. Currently, all features available on the Apillon platform are free of charge. In the later stages, the basic Web3 services will remain free, while the more advanced features will be available through unified stable pricing.
Certain tokens can be staked by locking them up for a period of time for various reasons, e.g. to support the operation of a Web3 project or network, in exchange for a reward, typically an interest on the staked amount.
Launched on January 26, 2023, the first invitation-only release of the Apillon platform provides Web3 Hosting and Storage services. The main purpose of the Apillon Closed Beta is to test and uncover potential bugs. Read more >
Apillon launched a referral program that will reward existing users for bringing more attention to the Apillon platform among their communities and peers, thus facilitating broad Web3 adoption.
Apillon Lightpaper states the challenges developers face when building Web3 projects and how the Apillon platform can help solve them.
Become part of the Apillon community and stay up to date with the latest news.
On June 19, on the encouragement of the key ecosystem stakeholders, Apillon submitted an OpenGov discussion to back upcoming proposal for Polkadot Treasury funding.
Backed by Polkadot Treasury funding and community support, the Apillon platform would transition to an open-source product with permanent freemium access and community governance. By becoming a common good project, Apillon would help spur Web3 adoption, Polkadot ecosystem growth, and DOT utility.
Here are the most frequent questions and answers to help you navigate and understand the weight of the proposal.
Polkadot’s common good and open-source approach aims to innovate services that benefit projects and users across the whole ecosystem.
With many new protocol-layer projects regularly developed on Polkadot, the ecosystem is in dire need of an application-layer solution that would allow developers to easily onboard the Polkadot ecosystem and bring Web3 to a mainstream level.
With Apillon transforming into a common good solution, the Polkadot ecosystem would get a forever-freemium Web3 development platform. Every new user on the Apillon platform would mean a new developer for Polkadot, boosting the adoption of Web3 technologies. Through DOT-based governance, the Polkadot’s native token would get another utility boost and purpose in driving the network’s adoption.
With decentralized governance, existing and new platform users would get to decide on parachain integrations and the evolution of the Apillon platform, thus collaboratively innovating the future of Web3.
Apillon would make a strong commitment to the community and the ecosystem to achieve the set milestones, spend resources as described in the Proposal, and pursue the vision of the developer community.
Apillon’s Proposal for Polkadot’s Treasury funding seeks community support to transform into an open-source infrastructure on Polkadot.
The funding would have the following direct consequences on Apillon:
With operational costs covered by the Treasury for two years, Apillon would be able to incentivize developers with freemium plans at no additional fees.
After the end of the two-year period, Apillon would introduce a price margin on top of parachain service cost. However, this would be non-profitable and controlled via governance, allowing the Apillon team to survive on fees to keep the project alive and in the best possible shape.
Apillon’s Treasury-backed transition to open-source presents a strong obligation towards the community. Therefore, The Freemium plan will remain permanently or until the project’s governance decides otherwise.
In the current stage, three main pillars of Apillon would require implementation of decentralized control:
With DOT-based governance, the developer community would be able to co-decide the development direction of the Apillon platform, making it more agile in addressing market demands.
Apillon’s product governance would address questions such as:
The Apillon platform would further improve the Polkadot ecosystem and the Web3 space with:
Easier, faster, and cheaper Web3 development would bring developers and other stakeholders:
Apillon's contribution to Web3 adoption and the growth of the Polkadot ecosystem is significant and lies in its provision of an open-source common good development platform. A user-friendly and accessible environment for building Web3 applications would attract more developers, including those transitioning from Web2 to Web3. The influx of talent and ideas would fuel the creation of diverse and innovative decentralized applications built on the Polkadot network, leading to increased usage, adoption, and overall ecosystem growth. Additionally, by providing a streamlined development experience and facilitating the integration of various parachains, Apillon's platform contributes to the scalability and interoperability of the network, making it more appealing to developers and users alike.
The backing of Polkadot Treasury funding would tremendously impact the Apillon platform. It would provide financial support and resources to develop further and enhance the platform's capabilities, enabling Apillon to accelerate its efforts in building a robust Web3 infrastructure. The funding support would help Apillon attract top talent, foster innovation, and execute its roadmap effectively, ultimately contributing to the growth and adoption of Web3 technologies within the Polkadot ecosystem.
The opportunity for Apillon to become an open-source common good infrastructure platform on Polkadot opens up opportunities for early contributors, too.
Polkadot Treasury funding of the Apillon two-and-a-half-year development ensures uninterrupted work on both preset and jointly agreed upon milestones to deliver the most requested and needed Web3 services and features to the developer community.
With every developer using the Apillon platform, the Polkadot and Web3 ecosystems gain a new contributor. Faster growth of Apillon means accelerated expansion and collaboration of both established and up-and-coming Web3 projects.
In addition to the financial aspect, Polkadot Treasury would provide a much-appreciated approval from the wider Web3 community, acknowledging Apillon as the go-to Web3 development platform - that would significantly increase the future value of the project.
Apillon has always greatly appreciated the community support and trust in the project.
At this early point of the referendum process, the benefits for early investors are still undefined. The Apillon team kindly asks for everyone’s patience while the optimal options for the community are being explored.
One of Proposal’s main points is Apillon transitioning to DOT-based governance and operations. Hence, the launch of the NCTR token is postponed until after the end of the period set in the Proposal.
The official discussion on the Apillon Proposal will be taking place on Polkassembly. You can engage with the broader Web3 and Polkadot community on the Polkadot Forum as well.
The public discussion will be open for a month before the voting process begins on July 17, 2023, lasting 28 days. Follow our social media channels and stay updated on the latest developments.
If you are a DOT holder, you will be able to vote with your Polkadot.js wallet, Talisman, or Subwallet on Polkassembly once the proposal moves into the voting period.
If you’re not sure how to navigate the voting process, stay tuned for detailed voting tutorials.
Do you care about boosting Web3 adoption? Providing access to easier, cheaper, and faster Web3 development for all developers? Expanding the possibilities for the growth of Polkadot’s ecosystem?
If you do, you should vote Aye on the Apillon’s Proposal.
If you have second thoughts, please share them with our team on Polkassembly right below the post or on Polkadot Forum. We’ll be happy to answer all your questions.
The Proposal suggests a timeline for Polkadot Treasury funding that spans three milestone periods and ends in Q3 2025.
If the Polkadot community ends up voting Nay on Apillon receiving Treasury funding, the work for the team will not cease.
Aligned with the preset milestones and following the dynamic backlog of integrations, the team will continue working towards developing and delivering state-of-the art features and services to drive Web3 adoption for every developer.
Discover more answers to community questions and join the discussion on Polkassembly.
The NCTR token (pronounced “nectar”) is Apillon’s native utility token and sustains Apillon functionalities when interacting with linked Polkadot parachains and transacting with their protocols.
It represents a uniform means of value exchange in the process of building Web3 projects on the Apillon platform, as it replaces the need to interact with individual tokens of utilized parachains.
For every value-based action on Apillon, the platform manages the utility of parachains’ features in their native tokens in the back end so that developers only deal with NCTR and do not have to worry about acquiring or exchanging other tokens.
The Apillon business model also supports payments in fiat (EUR and USD), further boosting adoption among first-time adopters of Web3. The platform converts it to NCTR to unlock Apillon functionalities.
The tokenomics of the NCTR token and Apillon’s business model rely on decision-making by two main actors:
The NCTR tokenomics are, therefore, built to incentivize both actors and ensure all NCTR holders have a relevant say in the platform’s future and get rewarded for participating in Apillon’s governance.
Developers are the force that drives Web3 and the first group of participants in the Apillon tokenomics. The platform is designed to cater to their ever-evolving needs, respond to their expectations, challenges, and workflows, but also to empower them to voice feedback and impact the course of platform development through governance.
In the vast and ever-evolving field of blockchain technology, it is increasingly challenging for all stakeholders to track all the newly launched projects and evaluate their offerings. To double down on the Web3 research power, Apillon empowers its main users, the developers, to list new service candidates via governance and vote on preferred services to be implemented in the upcoming platform upgrades. Through user-powered governance and a token-curated registry of services, the platform continues to evolve and optimize its offering while preventing any potential censorship.
No Web3 project can stand the test of time and market without community backing. The Apillon community gathers NCTR token holders and Web3 developers. The NCTR token is designed to serve and reward everyone who contributes to the growth of the Apillon platform and boosts Web3 adoption, aligned with Apillon’s primary mission. All NCTR token holders can be Web3 builders, either by developing functional Web3 products or growing the ecosystem, and thus have the right to contribute to the NCTR token pledging and Apillon’s inclusive governance.
The NCTR token is designed to be a multi-purpose and widely used asset, covering a range of utility cases for platform users and community members. NCTR token utilities are spanning across three main pillars:
Each pillar further serves both NCTR Developers and NCTR Holders, offering distinct opportunities for utilizing the NCTR token.
Apillon's product governance does not imply an establishment of Apillon as an end-to-end DAO where participants propose and vote on everything, business decisions included. Instead, the governance aspect is focused on the product, i.e., the Apillon platform, and its narrow scope of user-oriented services and features that participants can help control and upgrade.
The second NCTR token’s utility is aimed at incentivizing both tokenomics actors in various ways while promoting Web3 adoption. Adoption is, therefore, the main measured KPI, and when achieved, it awards NCTR tokens to rightful recipients.
Incentivization through NCTR spans several use cases, including proof of adoption (PoA), prebuilt solutions grants, prebuilt solutions marketplace, SDK grands, protocol integration grants, bug bounties, authorship royalties, referral earnings, and many more.
Besides providing few-click-deploy Web3 services and straightforward APIs to integrated parachains, Apillon offers an underlying payments module that simplifies the use of Web3 technologies. The payments module removes the need for developers to acquire and maintain a positive balance of tokens to pay for underlying services. This way, Apillon can charge for its services in a traditional Saas model, where users pay monthly subscriptions with fiat using credit cards and unlock access to a fixed amount of Web3 services and features. The secondary path to pay for Apillon services is through the NCTR token. The Governance and Incentivization utilities of the NCTR token deliver a fantastic opportunity for synergy between NCTR payments and product governance.
The NCTR token is an ERC-20-compatible token minted on the Astar network.
Total supply: 150,000,000 NCTR
Learn more about the NCTR token, its role in the Apillon business model, and the general tokenomics.
The list of endpoints where API is available:
Environment | URL |
---|---|
Production | https://api.apillon.io |
Testing | Coming soon... |
Apillon API is a set of RESTful API endpoints allowing developers to integrate Apillon modules into their Web3 applications.
Unless clearly marked as public, all routes are private and require an API key.
Every request goes through authentication middleware, where the following errors can occur:
Status | Message | Description |
---|---|---|
400 | Missing Authorization header | Request is missing authorization header. |
400 | Malformed Authorization header | Authorization header field has an invalid form. |
401 | Invalid API key or API key secret | Authorization header is valid, but credentials in it are not. |
Each endpoint requires a certain role or permission from the API key.
There are three types of permissions that could be assigned to an API key:
Code | Name | Description |
---|---|---|
50 | KEY_EXECUTE | Permission to execute certain actions |
51 | KEY_WRITE | Permission to create, modify or delete records |
52 | KEY_READ | Permission to read record |
These permissions could be assigned to an API key for every attached service (e.g., Web3 Storage (Crust), Web3 Authentication (KILT), etc.).
If a request is made with an API key that lacks permission for a called endpoint, the following errors can occur:
Status | Message | Description |
---|---|---|
403 | Insufficient permissions - missing permission name permission | API key lacks required permission for called service. |
403 | Insufficient permissions to access this record | API key has required permissions for endpoint, but it does not have the right to access the addressed record (i.e., a record belongs to a different project). |
Endpoints starting with "List" are intended to list different data, where the response contains the below properties.
Name | Description |
---|---|
items | Records on a specified page that match the current query |
total | Number of all records that match the query |
limit | Number of items on a page (default: 20). |
page | Current page |
Listing endpoints by default supports the query parameters below:
Name | Description | Required |
---|---|---|
search | Search the items usually by name or some other property specifying this item. | false |
page | Items are paginated by default. This parameter is used to get items from a specific page. | false |
limit | Number of items on a page (default: 20). | false |
orderBy | One or multiple properties, separated by a comma, used to order data. | false |
desc | Boolean values, mapped to the index of the orderBy parameter. Defaults to false. | false |
status | Integer values, to filter by the entity's status (each entity has corresponding status codes) | false |
Every response has a unique ID, which helps identify potential issues. It also includes a status code that can help identify the cause of a potential problem.
Query requests through the GET
method can return status codes 200
, 400
, 401
, 403
, or 500
. Mutations through POST
, PUT
, and DELETE
can also return codes 201
and 422
. Invalid routes return status code 404
.
A successful request includes a data
key, which holds a valid response object.
List of responses:
A request fails if response code is not 200 or 201. The Apillon API returns two types of errors.
Errors include a unique code number, a property that caused the error, and an error message. The code number helps identify potential issues and points to their exact position in the system.
Fields in code exception:
',7),j={class:"split_content"},C=n('Field | Description |
---|---|
id | Unique ID of request |
code | Apillon API internal error code pointing to the exact position in the system |
message | Message describing the error |
path | Endpoint that threw the error |
timestamp | Date when the error occurred |
Fields in validation exception:
Field | Description |
---|---|
id | Unique ID of request |
model | Apillon API model used to validate request payload |
errors | Array of errors |
path | Endpoint that threw the error |
timestamp | Date when the error occurred |
Through the whole Apillon API, the same errors can occur. The reason behind it can be current subscription package limits or current credit balance.
One of the limits based on the project subscription package is available storage space (on the IPFS node). If a project reaches the storage space limit, the following error will occur.
{
+ ...
+ "code": 40006003,
+ "message": "NOT_ENOUGH_STORAGE_SPACE",
+ ...
+}
+
Some nonrecurrent actions require payment with credits. If a project's credit balance is lower than price of executed action, API will return status 402 and the following response.
{
+ ...
+ "code": 40210000,
+ "message": "CREDIT_BALANCE_TOO_LOW",
+ ...
+}
+
Api key is created inside a project and can be used to get project details through Apillon API.
`,14),U={class:"split_content"},G=n('API to get project credit balance
Name | Type | Description |
---|---|---|
balance | number | Current credit balance - amount of credits in project, that can be used to perform different actions |
This package provides Dart and Flutter developers with tools and libraries to interact with Apillon services, simplifying the use of Apillon's REST API by reducing boilerplate code and streamlining multi-step processes into single operations.
dependencies:
+ apillon_flutter: ^0.0.1
+
To start using the SDK, you need to import it and configure it with your Apillon API key and secret. Here's an example of how to initialize the Storage module:
import 'package:apillon_flutter/apillon_flutter.dart';
+
+void main() {
+ var storage = Storage(ApillonConfig(
+ key: 'yourApiKey',
+ secret: 'yourApiSecret',
+ ));
+}
+
All modules in the Apillon Flutter SDK require the same initial configuration of key
and secret
.
The Apillon Flutter SDK consists of several modules, each corresponding to a specific Apillon service. Below are examples of how to use some of these modules.
The Storage module provides functionalities for interacting with the Storage service.
import 'dart:io';
+import 'package:apillon_flutter/apillon_flutter.dart';
+import 'package:path/path.dart' as path;
+
+void main() async {
+ var storage = Storage(ApillonConfig(
+ key: 'yourApiKey',
+ secret: 'yourApiSecret',
+ ));
+
+ // List all buckets
+ var buckets = await storage.listBuckets(IApillonPagination());
+ print('Buckets:');
+ for (var bucket in buckets) {
+ print('\${bucket.name} - \${bucket.uuid}');
+ }
+
+ var bucketUuid = 'eaff2672-3012-46fb-9278-5efacc6cb616';
+
+ // Get specific bucket details
+ var bucketDetails = await storage.bucket(bucketUuid).get();
+ print('Bucket Details: \${bucketDetails.name}, Size: \${bucketDetails.size}');
+
+ // List files in the bucket
+ var files = await storage.bucket(bucketUuid).listFiles(IBucketFilesRequest());
+ print('Files in bucket:');
+ for (var file in files) {
+ print('\${file.name} - \${file.uuid}');
+ }
+
+ // Upload files from a folder
+ var uploadDir = path.join(Directory.current.path, 'my-folder');
+ print('Uploading files from $uploadDir');
+ await storage.bucket(bucketUuid).uploadFromFolder(uploadDir, IFileUploadRequest());
+
+ // Upload a single file from buffer
+ var filePath = path.join(Directory.current.path, 'file.txt');
+ var fileBytes = File(filePath).readAsBytesSync();
+ await storage.bucket(bucketUuid).uploadFiles([
+ FileMetadata(
+ fileName: 'file.txt',
+ contentType: 'text/plain',
+ content: fileBytes,
+ )
+ ], IFileUploadRequest());
+
+ // Get details of a specific file
+ var fileUuid = 'eaff2672-3012-46fb-9278-5efacc6cb616';
+ var fileDetails = await storage.bucket(bucketUuid).file(fileUuid).get();
+ print('File Details: \${fileDetails.name}, Size: \${fileDetails.size}');
+}
+
The Storage module additionally contains methods for manipulating IPNS records for a specific storage bucket.
import 'package:apillon_flutter/apillon_flutter.dart';
+
+void main() async {
+ var storage = Storage(ApillonConfig(
+ key: 'yourApiKey',
+ secret: 'yourApiSecret',
+ ));
+ var bucketUuid = 'eaff2672-3012-46fb-9278-5efacc6cb616';
+
+ // List all existing IPNS records in a bucket
+ var ipnsRecords = await storage.bucket(bucketUuid).listIpnsNames(IPNSListRequest());
+ print('IPNS Records:');
+ for (var record in ipnsRecords) {
+ print('\${record.name} - \${record.uuid}');
+ }
+
+ // Create a new IPNS record
+ const name = 'Test IPNS';
+ const description = 'This is a test description';
+ const cid = 'QmUxtfFfWFguxSWUUy2FiBsGuH6Px4KYFxJqNYJRiDpemj';
+ var newIpnsRecord = await storage.bucket(bucketUuid).createIpns(ICreateIpns(
+ name: name,
+ description: description,
+ cid: cid,
+ ));
+ print('New IPNS Record: \${newIpnsRecord.uuid}');
+
+ // Publish an IPNS record to point to a new CID
+ const newCid = 'Qmakf2aN7wzt5u9H3RadGjfotu62JsDfBq8hHzGsV2LZFx';
+ await storage.bucket(bucketUuid).ipns(newIpnsRecord.uuid).publish(newCid);
+ print('IPNS record published to new CID: $newCid');
+
+ // Delete an IPNS record
+ await storage.bucket(bucketUuid).ipns(newIpnsRecord.uuid).delete();
+ print('IPNS record deleted: \${newIpnsRecord.uuid}');
+}
+
The NFT module encapsulates functionalities for the NFT service.
import 'package:apillon_flutter/apillon_flutter.dart';
+
+void main() async {
+ var nft = Nft(ApillonConfig(
+ key: 'yourApiKey',
+ secret: 'yourApiSecret',
+ ));
+
+ // Create a new NFT collection
+ var collection = await nft.create(ICreateCollection(
+ chain1: EvmChain.moonbase,
+ collectionType1: CollectionType.generic,
+ name: 'Space Explorers',
+ description: 'A collection of space explorers',
+ symbol: 'SE',
+ royaltiesFees: 3,
+ royaltiesAddress: '0x95B8c6b9225456107649776EF8aAF20C42d58814',
+ baseUri: 'https://test.com/metadata/',
+ baseExtension: '.json',
+ maxSupply: 50,
+ isRevokable: false,
+ isSoulbound: false,
+ drop: false,
+ ));
+ print('Collection created: \${collection.uuid}');
+ // or create a substrate collection
+ var substrateCollection = await nft.createSubstrate({
+ chain1: SubstrateChain.astar,
+ collectionType1: CollectionType.generic,
+ name: 'SpaceExplorers',
+ symbol: 'SE',
+ ...
+ });
+
+ // Mint a new NFT in the collection
+ var mintResult = await nft.collection(collection.uuid).mint(IMintNftData(
+ receivingAddress: '0x5BA8B0c24bA5307b67E619ad500a635204F73bF1',
+ quantity: 1,
+ ));
+ print('Mint transaction hash: \${mintResult.transactionHash}');
+
+ // List NFT collections
+ var collections = await nft.listCollections(ICollectionFilters());
+
+ // Transfer NFT ownership to another address
+ await collection.transferOwnership('0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD');
+}
+
The Identity module provides functionalities for validating wallet signatures and fetching identity data.
import 'package:apillon_flutter/apillon_flutter.dart';
+
+void main() async {
+ var identity = Identity(ApillonConfig(
+ key: 'yourApiKey',
+ secret: 'yourApiSecret',
+ ));
+
+ // Generate a signing message for EVM wallet signature validation
+ const customMessage = 'Identity EVM SDK test';
+ var signingMessage = identity.generateSigningMessage(customMessage)["message"];
+ print('Signing message: $signingMessage');
+
+ var walletAddress = '0xa79bg13g2...';
+ var signature = '0xYourSignature'; // signature obtained from the user's wallet by the client app
+
+ // Validate EVM wallet signature
+ var validationResult = await identity.validateEvmWalletSignature(IValidateEvmWalletSignature(
+ walletAddress: walletAddress,
+ message: signingMessage,
+ signature: signature,
+ ));
+ print('Is valid: \${validationResult.isValid}');
+ print('Address: \${validationResult.address}');
+
+ // Get wallet identity profile for a Polkadot address
+ var polkadotAddress = '5HqHQDGcHqS...',
+ var identityProfile = await identity.getWalletIdentity(polkadotAddress);
+ print('Identity Profile: \${identityProfile.subsocial['content']['name']}');
+}
+
In all cURL examples, parameters with a colon as a prefix should be replaced with real values.
File upload process through Apillon Web3 Storage API
Name | Description | Required |
---|---|---|
bucketType | Type of bucket: 1 (storage bucket), 2 (website bucket) and 3 (nft bucket). | false |
Each item is an instance of the bucket model with the below properties:
Field | Type | Description |
---|---|---|
createTime | DateTime | Item create time |
updateTime | DateTime | Item last update time |
bucketUuid | string | Bucket unique identifier |
bucketType | integer | Item type with possible values 1 (storage bucket), 2 (website bucket) and 3 (nft bucket) |
name | string | Bucket name |
description | string | Bucket description |
size | integer | Size of bucket in bytes |
Name | Type | Description | Required |
---|---|---|---|
name | string | Bucket name. | true |
description | string | Bucket description. | false |
Code | Description |
---|---|
42200003 | Request body is missing a name field. |
A response is an instance of bucket, described above.
Name | Description | Required |
---|---|---|
bucketUuid | Unique key of storage bucket. Key is displayed on developer dashboard. | true |
Name | Type | Description | Required |
---|---|---|---|
files | array | Array of files metadata. Maximum 200 items. | true |
sessionUuid | string | Session unique key, which must be specified to add more uploads to existing session. | false |
Each metadata object in the files
array contains the properties below.
Code | Description |
---|---|
40406002 | Bucket does not exist. |
40406009 | Bucket is marked for deletion. It is no longer possible to upload files to it. |
40006020 | HTML files cannot be uploaded to storage bucket in freemium subscription plan. |
42200040 | Request body is missing a files field. |
42200150 | files has invalid length. It should be between 1 and 200 |
42200008 | Request body file object is missing a fileName field. |
50006003 | Internal error - Apillon was unable to generate upload URL. |
Name | Type | Description |
---|---|---|
sessionUuid | string | Session unique key, which is later used to end upload and transfer files to bucket |
files | array | Array of files metadata. |
Files in the request body are returned in response data.files
property. Each file is equipped with url
and fileUuid
. All properties are displayed below.
Name | Description | Required |
---|---|---|
bucketUuid | Unique key of bucket. Key is displayed in developer dashboard. | true |
sessionUuid | Session uuid, recieved in upload to bucket | true |
Name | Type | Description | Required |
---|---|---|---|
wrapWithDirectory | boolean | Wrap uploaded files to IPFS directory | false |
directoryPath | string | Path to wrapped directory inside bucket | false |
Code | Description |
---|---|
40406004 | Session does not exist |
40006001 | Files in this session were already transferred |
API responds with the status 200 OK
if operation is successfully executed.
Name | Description | Required |
---|---|---|
bucketUuid | Unique key of bucket. Key is displayed on developer dashboard. | true |
Name | Description | Required |
---|---|---|
directoryUuid | Gets items inside a specific directory. | false |
markedForDeletion | Include deleted buckets | false |
Code | Description |
---|---|
40406002 | Bucket does not exist. |
Properties of each item:
Field | Type | Description |
---|---|---|
uuid | string | Item UUID property |
type | integer | Item type with possible values 1 (directory) and 2 (file) |
name | string | Item (directory or file) name |
CID | string | File content identifier - label used to point to content in IPFS. |
createTime | DateTime | Item create time |
updateTime | DateTime | Item last update time |
contentType | string | Item content type (MIME type). |
size | integer | Item size in bytes |
directoryUuid | string | Uuid of directory where the file directory is located |
link | string | Link on IPFS gateway. |
fileStatus | number | Current status of file |
Name | Description | Required |
---|---|---|
bucketUuid | Unique key of bucket. Key is displayed on developer dashboard. | true |
Code | Description |
---|---|
40406002 | Bucket does not exist. |
Properties of each item:
Field | Type | Description |
---|---|---|
createTime | DateTime | File create time |
updateTime | DateTime | File last update time |
fileUuid | string | File UUID property |
CID | string | File content identifier - label used to point to content in IPFS. |
name | string | File name |
contentType | string | File content type. Value is taken from file upload request |
path | integer | Full path to file |
size | integer | File size in bytes |
fileStatus | number | File statuses are described in below table |
link | string | Link on IPFS gateway. |
Name | Description | required |
---|---|---|
bucketUuid | Unique key of a bucket. Key is displayed on developer dashboard. | true |
fileUuid | File UUID or CID. | true |
Code | Description |
---|---|
40406005 | File does not exist. |
Field | Type | Description |
---|---|---|
createTime | DateTime | File create time |
updateTime | DateTime | File last update time |
fileUuid | string | File UUID property |
CID | string | File content identifier - label used to point to content in IPFS. |
name | string | File name |
contentType | string | File content type. Value is taken from file upload request |
path | integer | Full path to file |
size | integer | File size in bytes |
fileStatus | number | File statuses are described in below table |
directoryUuid | string | Uuid of directory where the file is located |
link | string | Link on IPFS gateway. |
Number | Description |
---|---|
1 | Request for upload to Apillon storage was generated. |
2 | File is uploaded to Apillon central server. |
3 | File is transferred to the IPFS node. |
4 | File is replicated to different IPFS nodes through Crust Network. |
Name | Description | required |
---|---|---|
bucketUuid | Unique key of bucket. Key is displayed on developer dashboard. | true |
fileUuid | File unique identifier. | true |
Code | Description |
---|---|
40406005 | File does not exist. |
40006009 | File is already marked for deletion. |
The response of the delete function is a boolean value, depends on whether the deletion was successful.
Name | Description | required |
---|---|---|
bucketUuid | Unique key of bucket. Key is displayed on developer dashboard. | true |
directoryUuid | Directory unique identifier. | true |
Code | Description |
---|---|
40406003 | Directory does not exist. |
40006007 | Directory is already marked for deletion. |
The response of the delete function is a boolean value, depends on whether the deletion was successful.
Field | Type | Description |
---|---|---|
secret | string | Secret for this project, which can be used to generate tokens to access content of IPFS gateway |
projectUuid | string | Project unique identifier |
ipfsGateway | string | Gateway that can used to access content via CIDs. |
ipnsGateway | string | Gateway that can be used to access content via IPNS name. |
Name | Description | required |
---|---|---|
cid | Ipfs content identifier. API will automatically detect the type (CIDv0, CIDv1 or IPNS) | true |
Field | Type | Description |
---|---|---|
link | string | Link where requested content can be accessed. |
The JWT sign method expects three (3) parameters:
{
+ "cid": "CID or IPNS address",
+ "project_uuid": "Change with projectUuid value"
+}
+
secret
property from IPFS cluster infoIPFS-token
For each CID, a new token should be generated. Append the generated JWT to URL request as token
query parameter.
Name | Description | Required |
---|---|---|
bucketUuid | Unique key of storage bucket, from which IPNS will be listed | true |
Name | Description | Required |
---|---|---|
ipnsName | List IPNS names with specific name | false |
ipnsValue | List IPNS names that point to this value (CID) | false |
Each item is an instance of the IPNF model, with the following properties:
Field | Type | Description |
---|---|---|
createTime | DateTime | Item create time |
updateTime | DateTime | Item last update time |
ipnsUuid | string | IPNS unique identifier |
name | string | Informational IPNS name, which is set by a user to easily organize the IPNS records |
description | string | IPNS description |
ipnsName | string | IPNS name used to access IPNS content on IPFs gateway |
ipnsValue | string | IPFS value (CID), to which this ipns points |
link | string | IPNS link to Apillon IPFS gateway, allowing to access content to which this IPNS points |
Name | Description | Required |
---|---|---|
bucketUuid | Unique key of storage bucket where IPNS will be created. Key is displayed on developer dashboard. | true |
Name | Type | Description | Required |
---|---|---|---|
name | string | IPNS name. | true |
description | string | IPNS description. | false |
cid | string | CID to which the IPNS name will point. If this property is specified, API executes IPNS publish, which sets the ipnsName and ipnsValue properties. | false |
Code | Description |
---|---|
42200026 | Request body is missing a name field. |
40406002 | Bucket not found |
Response is an instance of IPNS described above.
Name | Description | Required |
---|---|---|
bucketUuid | Unique key of storage bucket | true |
ipnsUuid | Unique key of IPNS name | true |
Code | Description |
---|---|
40406012 | IPNS not found |
Response is an instance of IPNS described above.
Name | Description | Required |
---|---|---|
bucketUuid | Unique key of storage bucket. | true |
ipnsUuid | Unique key of IPNS record that will be published | true |
Name | Type | Description | Required |
---|---|---|---|
cid | string | CID to which the IPNS name will point. | true |
Code | Description |
---|---|
42200030 | Body is missing CID property |
40406012 | IPNS not found |
The response is an instance of IPNS that was published. Properties are described above.
Name | Description | Required |
---|---|---|
bucketUuid | Unique key of storage bucket | true |
ipnsUuid | Unique key of IPNS record | true |
Code | Description |
---|---|
40406012 | IPNS not found |
The response is deleted IPNS record, an instance of IPNS described above.
To streamline the development experience, Apillon Web3 Storage service further introduces the concept of storage buckets.
A storage bucket is a virtual container that holds directories and files in a hierarchical structure. Each directory can contain multiple subdirectories and multiple files, and so on for each subdirectory.
Before using the Apillon Web3 Storage service, a storage bucket should be created on the Apillon Dashboard. Once ready, it enables file storage from both the Apillon dashboard and the API endpoints.
The process below describes how files are stored with Apillon storage buckets.
Each file hosting that passes through Crust’s pinning and replication service is paid upfront for a minimum period of 6 months. Apillon has no control over amending that period, so keep in mind that all files you deploy to Apillon storage buckets will remain accessible for that period of time.
If you decide to delete a file before the 6-month period expires, the file is marked for deletion. This means that Apillon does not extend the storage lease on Crust once the 6-month period expires, which leads to file deletion on all IPFS instances.
However, to make the storage service more dynamic, Apillon artificially lowers the file deletion period to 3 months. Once this period expires, the load of deleted files in your storage bucket is emptied, and the storage capacity is made available for uploading new files.
Note: These limitations are in the nature of the Apillon Closed Beta release, which is intended for testing purposes only. Once the Beta period is up, the limitations of Apillon Web3 services will be adjusted to more realistic production requirements.
',12),u={href:"https://blog.apillon.io/faq-apillon-web3-storage-c99a9b0e8b12",target:"_blank",rel:"noopener noreferrer"};function b(g,m){const o=a("ExternalLinkIcon");return n(),s("div",null,[c,t("p",null,[e("Apillon Web3 Storage is a Web3-based storage service that implements AWS S3 (as cache to optimize upload of large files), "),t("a",h,[e("IPFS"),i(o)]),e(", and "),t("a",p,[e("Crust Network"),i(o)]),e(" (to pin files on multiple IPFS nodes).")]),f,t("p",null,[e("Learn more: "),t("a",u,[e("Web3 Storage FAQ"),i(o)])])])}const k=r(d,[["render",b],["__file","2-web3-storage.html.vue"]]);export{k as default}; diff --git a/assets/2-what-is-apillon.html-23c88ace.js b/assets/2-what-is-apillon.html-23c88ace.js new file mode 100644 index 00000000..9fc5417b --- /dev/null +++ b/assets/2-what-is-apillon.html-23c88ace.js @@ -0,0 +1 @@ +const a=JSON.parse('{"key":"v-d64fcca0","path":"/about/2-what-is-apillon.html","title":"What is Apillon?","lang":"en-US","frontmatter":{},"headers":[],"git":{"updatedTime":1715328683000,"contributors":[{"name":"Damjan Dimitrov","email":"damjandimitrov1@gmail.com","commits":1}]},"filePathRelative":"about/2-what-is-apillon.md"}');export{a as data}; diff --git a/assets/2-what-is-apillon.html-65077af6.js b/assets/2-what-is-apillon.html-65077af6.js new file mode 100644 index 00000000..4723e9cb --- /dev/null +++ b/assets/2-what-is-apillon.html-65077af6.js @@ -0,0 +1 @@ +import{_ as t,o as n,c as o,b as e,e as s}from"./app-270cc6ec.js";const i={},a=e("h1",{id:"what-is-apillon",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#what-is-apillon","aria-hidden":"true"},"#"),s(" What is Apillon?")],-1),l=e("div",{class:"divider"},null,-1),d=e("p",null,"Apillon is a Web3 development platform for developers and businesses building on Web3. By gathering unified API endpoints, it delivers the functionalities of Polkadot parachains adapted to the standard development process and user-preferred technology stack.",-1),r=e("p",null,"It gathers a unique set of tools, ample SDKs, and comprehensive documentation to fully support users in building their Web3 products from day one.",-1),c=e("p",null,"With Apillon, developers can create a complex and fully-functional Web3-based product in just days, harnessing the services provided by different parachains but without the need to employ them individually and from scratch or to spend years mastering the technical knowledge.",-1),h=e("p",null,"This way, they can focus more on the functionalities of their Web3 product than the underlying blockchain complexity and drastically shorten its go-to-market timeline.",-1),p=[a,l,d,r,c,h];function u(_,f){return n(),o("div",null,p)}const y=t(i,[["render",u],["__file","2-what-is-apillon.html.vue"]]);export{y as default}; diff --git a/assets/3-hosting-api.html-6ebd1a24.js b/assets/3-hosting-api.html-6ebd1a24.js new file mode 100644 index 00000000..9e45eb61 --- /dev/null +++ b/assets/3-hosting-api.html-6ebd1a24.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-3fe44474","path":"/build/3-hosting-api.html","title":"Hosting API","lang":"en-US","frontmatter":{},"headers":[{"level":3,"title":"List websites","slug":"list-websites","link":"#list-websites","children":[]},{"level":3,"title":"Get website","slug":"get-website","link":"#get-website","children":[]},{"level":3,"title":"Get URLs for files upload","slug":"get-urls-for-files-upload","link":"#get-urls-for-files-upload","children":[]},{"level":3,"title":"End upload session","slug":"end-upload-session","link":"#end-upload-session","children":[]},{"level":3,"title":"Deploy website","slug":"deploy-website","link":"#deploy-website","children":[]},{"level":3,"title":"List website deployments","slug":"list-website-deployments","link":"#list-website-deployments","children":[]},{"level":3,"title":"Get deployment","slug":"get-deployment","link":"#get-deployment","children":[]}],"git":{"updatedTime":1715328683000,"contributors":[{"name":"Damjan Dimitrov","email":"damjandimitrov1@gmail.com","commits":1}]},"filePathRelative":"build/3-hosting-api.md"}');export{e as data}; diff --git a/assets/3-hosting-api.html-e0ef4a3a.js b/assets/3-hosting-api.html-e0ef4a3a.js new file mode 100644 index 00000000..4aff45eb --- /dev/null +++ b/assets/3-hosting-api.html-e0ef4a3a.js @@ -0,0 +1,251 @@ +import{_ as p,r,o as u,c as b,b as e,e as s,d as t,w as n,f as h,a as l}from"./app-270cc6ec.js";const m={},k=e("h1",{id:"hosting-api",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#hosting-api","aria-hidden":"true"},"#"),s(" Hosting API")],-1),v={href:"https://en.wikipedia.org/wiki/CI/CD",target:"_blank",rel:"noopener noreferrer"},y=e("ol",null,[e("li",null,"Upload website files to Apillon cloud server.")],-1),f=e("ul",null,[e("li",null,"Request URLs for files upload"),e("li",null,"Upload files to cloud server"),e("li",null,"Trigger transfer into website")],-1),g=e("ol",{start:"2"},[e("li",null,"Execute deployment to staging or production environment.")],-1),_=e("strong",null,"Note:",-1),w={href:"https://app.apillon.io/dashboard/service/hosting",target:"_blank",rel:"noopener noreferrer"},T=e("p",null,"In all cURL examples, parameters with a colon as a prefix should be replaced with real values.",-1),A=e("h3",{id:"list-websites",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#list-websites","aria-hidden":"true"},"#"),s(" List websites")],-1),x={class:"split_content"},U=l('Each item is an instance of website class, with below properties:
Field | Type | Description |
---|---|---|
websiteUuid | string | Website unique identifier |
name | string | Website name |
description | string | Website description |
domain | string | Website domain. This property needs to be specified, so that Apillon is able to create SSL Certificates for IPFS gateway |
bucketUuid | string | Uuid of bucket for file upload |
ipnsStaging | string | Staging IPNS. Set if deployment to staging environment exists |
ipnsProduction | string | Production IPNS. |
createTime | DateTime | Item create time |
updateTime | DateTime | Item last update time |
Name | Description | Required |
---|---|---|
websiteUuid | Website UUID, visible in developer console website overview | true |
Code | Description |
---|---|
40406010 | Website does not exists |
Response is an instance of website class, described above and additional properties described below. Those properties will receive value after deploy.
Field | Type | Description |
---|---|---|
w3StagingLink | string | Link to staging version of the website |
w3ProductionLink | string | Link to production version of the website |
lastDeploymentUuid | string | Website last deployment (to any environment) unique identifier |
lastDeploymentStatus | string | Status of last deployment |
Name | Description | Required |
---|---|---|
websiteUuid | Unique key of website bucket. Key is displayed on developer dashboard. | true |
Name | Type | Description | Required |
---|---|---|---|
files | array | Array of files metadata. | true |
sessionUuid | string | Session unique key. If not specified, API generates new one. It is possible to use same sessionUuid in multiple requests. | false |
Each file metadata object in files
array, contain below properties.
Code | Description |
---|---|
40406010 | Website does not exists |
42200040 | Request body is missing a files field. |
42200008 | Request body file object is missing a fileName field. |
50006003 | Internal error - Apillon was unable to generate upload URL. |
Name | Type | Description |
---|---|---|
sessionUuid | string | Session unique key, which is later used to end upload and transfer files to website. |
files | array | Array of files metadata. |
Files in request body are returned in response data.files
property. Each file is equipped with url
and fileUuid
. All properties are displayed below.
Name | Description | Required |
---|---|---|
websiteUuid | Unique key of website. | true |
sessionUuid | Session uuid, passed or recieved in get URL for upload request | true |
Code | Description |
---|---|
40406004 | Session does not exists |
40006001 | Files in this session were already transferred |
Api respond with status 200 OK
, if operation is successfully executed.
Name | Description | Required |
---|---|---|
websiteUuid | Website UUID, visible in developer console website overview | true |
Name | Type | Description | Required |
---|---|---|---|
environment | number | Possible environment values are explained below | true |
Status | Description |
---|---|
1 | Uploaded files are deployed to staging(preview) environment. Website will be available through staging IPNS link |
2 | Files from current staging environment are deployed to production environment. Website is pinned to CRUST, replicated and available through production IPNS link |
3 | Same as 2 , only that the source are uploaded files, not files in staging environment. |
Code | Description |
---|---|
42200039 | Request body is missing an environment field. |
40406010 | Website does not exists |
40006016 | There are no files to deploy. |
40006017 | There are no changes to deploy. |
Endpoint triggers deployment of website to specific environment. As result, deployment record with below field is returned. This deployment is now waiting to be processed.
Note: Deployment is processed in background, which may take several minutes. When deploying to staging
environment, files are added to IPFS and wrapped to directory, which is then accessible in IPFS via IPNS or CID. In production
, this CID is pinned to CRUST and replicated to other nodes.
Field | Type | Description |
---|---|---|
deploymentUuid | string | Deployment unique identifier |
environment | number | Environment to where website will be deployed |
deploymentStatus | number | Current status of deployment. Possible values are listed below. |
cid | string | When deployment is successful, CID points to directory on IPFS, where this page is accessible |
cidv1 | string | CID version 1 |
size | number | Size of website |
number | number | Deployment serial number - for this environment |
createTime | DateTime | Deployment create time |
updateTime | DateTime | Deployment last update time |
Deployment goes through different stages and each stage updates deploymentStatus
. Possible deployment statuses:
Status | Description |
---|---|
0 | Deployment initiated |
1 | In processing |
2 | In review |
3 | Website approved. Deployment will be executed |
10 | Deployment successful |
100 | Deployment failed |
101 | Deployment rejected |
Deployments (to environments 1 and 3) in projects without subscription go to review (deploymentStatus 3
). Review can take some time (up to 1 day) and if the website passes it, then the deployment continues.
Websites with illegal/phishing content goes to status 101
. The project owner is notified via mail and most likely banned from Apillon platform.
To speed up deployment process, make sure that project has one of subscription packages.
Name | Description | Required |
---|---|---|
deploymentStatus | Current deployment status | false |
environment | Deployment environment | false |
Each item in list is a deployment instance.
Name | Description | Required |
---|---|---|
websiteUuid | Website UUID, visible in developer console website overview | true |
deploymentUuid | Deployment unique identifier, returned from deploy website endpoint | true |
Code | Description |
---|---|
40406011 | Deployment does not exists |
Data
property is a deployment instance.
Note: At this point, only hosting of static websites is supported in Apillon Web3 Hosting service, while dynamic websites will be supported in future versions of Apillon.
The process below describes how a static website or app is hosted decentrally with Apillon Web3 Hosting.
To deploy a Web3 website or application, follow the process below:
',6),b={href:"https://app.apillon.io/register",target:"_blank",rel:"noopener noreferrer"},_=e("li",null,"Log in to your Apillon dashboard.",-1),m=e("li",null,'In the menu on the left, under Services, navigate to Hosting, and click "Get started."',-1),y=e("li",null,"Drag and drop your static website to the Hosting view and wait for the upload to finish.",-1),v=e("li",null,'Once the upload is complete and the status turns to "successful," you can deploy the website to Staging.',-1),k=e("li",null,"Click on the Staging tab to monitor the progress.",-1),A=e("li",null,'Deployment of web files will go through several statuses, ending with "successful."',-1),x=e("li",null,'Click "Deploy to production" to get files replicated and unstoppable with decentralized hosting.',-1),S=e("li",null,'Once the deployment to production is finished, click "Add domain" and "Configure domain" to make the domain you own point to the Apillon hosting.',-1),I=e("li",null,"Once DNS is updated, your unstoppable website will become available on the connected domain.",-1),H=e("p",null,"If you want to redeploy the website or app with new changes, repeat the process above simply by uploading the whole website or app via Apillon Hosting view.",-1),C=e("p",null,[e("strong",null,"Note:"),t(" Repeat deployment to Apillon Hosting will continue spending the Hosting storage capacity. However, every 3 months, the capacity will be renewed after older versions are deleted.")],-1),N={href:"https://blog.apillon.io/faq-apillon-web3-hosting-81d5477661e7",target:"_blank",rel:"noopener noreferrer"},W=e("h2",{id:"file-deletion",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#file-deletion","aria-hidden":"true"},"#"),t(" File deletion")],-1),z=e("p",null,"Each file hosting that passes through Crust’s pinning and replication service is paid upfront for a minimum period of 6 months. Apillon has no control over amending that period, so keep in mind that all files you deploy to Apillon storage buckets will remain accessible for that period of time.",-1),D=e("p",null,"If you decide to delete a file before the 6-month period expires, the file is marked for deletion. This means that Apillon does not extend the storage lease on Crust once the 6-month period expires, which leads to file deletion on all IPFS instances.",-1),F=e("p",null,"However, to make the hosting service more dynamic, Apillon artificially lowers the file deletion period to 3 months. Once this period expires, the load of deleted files in your storage bucket is emptied, and the storage capacity is made available for uploading new files and redeployment of website or app.",-1),L=e("strong",null,"Note",-1),T={href:"https://discord.gg/yX3gTw36C4",target:"_blank",rel:"noopener noreferrer"};function O(P,B){const s=n("RouterLink"),i=n("ExternalLinkIcon");return l(),r("div",null,[c,u,e("p",null,[t("Decentralized hosting of a website or an app on Apillon is very similar to the usage of storage buckets in "),o(s,{to:"/web3-services/1-good-to-know.html#web3-storage"},{default:d(()=>[t("decentralized storage")]),_:1}),t(". It implements AWS S3 (as cache to optimize upload of large files), "),e("a",g,[t("IPFS"),o(i)]),t(", and "),e("a",f,[t("Crust Network"),o(i)]),t(" (to pin files on multiple IPFS nodes).")]),w,e("ol",null,[e("li",null,[t("Register an "),e("a",b,[t("account on Apillon"),o(i)]),t(".")]),_,m,y,v,k,A,x,S,I]),H,C,e("p",null,[t("Learn more: "),e("a",N,[t("Web3 Hosting FAQ"),o(i)])]),W,z,D,F,e("p",null,[L,t(": In the Apillon Closed Beta stage, single file changes are not supported. Instead, hosting is treated as batch upload, meaning that with every new version of a website or app, its contents are rewritten, which leads to new files getting pinned and replicated on Crust. This limitation will be improved in future dashboard updates to enhance the developer experience. In case you need a larger storage capacity, feel free to get in touch on "),e("a",T,[t("Apillon Discord"),o(i)]),t(", and we will grant you extra space or more storage buckets.")])])}const R=a(p,[["render",O],["__file","3-web3-hosting.html.vue"]]);export{R as default}; diff --git a/assets/3-web3-hosting.html-91c06423.js b/assets/3-web3-hosting.html-91c06423.js new file mode 100644 index 00000000..1d438ee1 --- /dev/null +++ b/assets/3-web3-hosting.html-91c06423.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-f2d17dd2","path":"/web3-services/3-web3-hosting.html","title":"Web3 Hosting","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Website/app hosting","slug":"website-app-hosting","link":"#website-app-hosting","children":[]},{"level":2,"title":"Deployment","slug":"deployment","link":"#deployment","children":[]},{"level":2,"title":"File deletion","slug":"file-deletion","link":"#file-deletion","children":[]}],"git":{"updatedTime":1715328683000,"contributors":[{"name":"Damjan Dimitrov","email":"damjandimitrov1@gmail.com","commits":1}]},"filePathRelative":"web3-services/3-web3-hosting.md"}');export{e as data}; diff --git a/assets/3-why-apillon.html-22704871.js b/assets/3-why-apillon.html-22704871.js new file mode 100644 index 00000000..80ade5c1 --- /dev/null +++ b/assets/3-why-apillon.html-22704871.js @@ -0,0 +1 @@ +const i=JSON.parse('{"key":"v-0f48940c","path":"/about/3-why-apillon.html","title":"Why Apillon?","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Unified pricing","slug":"unified-pricing","link":"#unified-pricing","children":[{"level":3,"title":"Predictable pricing for greater adoption","slug":"predictable-pricing-for-greater-adoption","link":"#predictable-pricing-for-greater-adoption","children":[]},{"level":3,"title":"Predictable scaling","slug":"predictable-scaling","link":"#predictable-scaling","children":[]},{"level":3,"title":"Token utility, volatility, and predictable pricing paradox","slug":"token-utility-volatility-and-predictable-pricing-paradox","link":"#token-utility-volatility-and-predictable-pricing-paradox","children":[]},{"level":3,"title":"Multiple services at a single price tag","slug":"multiple-services-at-a-single-price-tag","link":"#multiple-services-at-a-single-price-tag","children":[]}]}],"git":{"updatedTime":1715328683000,"contributors":[{"name":"Damjan Dimitrov","email":"damjandimitrov1@gmail.com","commits":1}]},"filePathRelative":"about/3-why-apillon.md"}');export{i as data}; diff --git a/assets/3-why-apillon.html-f7d508e5.js b/assets/3-why-apillon.html-f7d508e5.js new file mode 100644 index 00000000..40f6ae45 --- /dev/null +++ b/assets/3-why-apillon.html-f7d508e5.js @@ -0,0 +1 @@ +import{_ as e,o as i,c as t,a}from"./app-270cc6ec.js";const o={},n=a('The dawn of open-source and decentralized technologies has inspired the creation of systems that give individuals more power to exchange assets and open up new business opportunities while reducing their reliance on centralized authorities. However, even though the pace of technological progress has been outstanding, the adoption of decentralized solutions still awaits implementation in the real economy on a large scale.
Polkadot, one of the leading players in the Web3 ecosystem, is set to transform the way businesses, individuals, and organizations manage assets and data. Still, several factors hinder the broader adoption of Web3 solutions:
Apillon's unified points of access deliver the functionalities of Polkadot parachains to developers simply through APIs, so they can create a working Web3-based product from day one while mitigating or avoiding the issues above.
While each parachain is best at solving its own isolated case, Apillon is all about connecting and curating different parachains, protocols, or pallets into the right context that can be easily adopted by developers to build tangible products in Web3.
Simplification further applies to pricing, as the platform takes care of all the underlying payment requirements with a unified payment system. Payment plans in DOT or fiat (EUR, USD) deliver price stability and predictability, ideal for enterprises and frequent users.
Building with Apillon does not require prior knowledge of blockchain technology and surpasses the hassle of lengthy testing and auditing. A streamlined product development substantially reduces the go-to-market timeline and resources spent in the process.
Apillon brings the power of distributed technologies to web developers and opens the floodgate to their widespread adoption. It democratizes the usability of advanced technologies and helps Web3 services reach wider and deeper.
Unpredictable pricing of Web3 services hinders adoption as users cannot foresee the costs generated in the process of using them. The Apillon platform delivers various Web3 services at stable pricing and allows users to devote more time and energy to developing new products.
The Web3 space delivers outstanding possibilities for building advanced decentralized products. However, the projects providing Web3 services have mostly focused on perfecting the technology offering and less on how end-users interact with it and adopt it.
One of the main challenges users need to overcome in Web3 services adoption is token/asset acquisition and management. This could seem like a minor issue when using a single service but could grow into an overwhelming toil when utilizing multiple at once.
"Apillon is a Web3 development platform that gathers and delivers complex blockchain solutions and protocols through a simplified and unified API endpoint, allowing developers to build Web3 projects quickly, effectively and with predictable pricing." Apillon Tokenomics Whitepaper
To deliver as smooth a user experience as possible, the Apillon platform performs automatic payments of underlying services in their native tokens and enables users to pay for all in a single transaction.
Each successful startup goes through typical development phases. First, after the initial idea, they build a prototype and MVP to create traction. Once they perfect their offering in the product-market fit phase, they work towards boosting growth in the scaling phase.
In supporting Web3 builders through their growth process, Apillon delivers a full-stack solution that allows both startups and established companies to develop and scale their products using Web3 building services.
Too often, as they work on perfecting the product-market fit, initial-stage businesses are not focused much on the costs that come with using different services. However, in the following phase, scaling the business model becomes a much more pragmatic issue, as it requires a lot of mid- to long-term planning and continuous cost optimization.
And this can only be achieved by utilizing services with reliable, predictable pricing models.
In the Web3 world, the pricing of most protocols, parachains, and services is typically based around a volatile token. This is directly correlated with project tokenomics, as they aim to boost token value with different token utilities, some more successfully, some less. Moreover, token valuation is heavily subject to market sentiment and community backing.
Charging for Web3 services in volatile tokens leads to unpredictable pricing models, which prevents their users from scaling successfully and reining their spending within rational range.
The unpredictability of service pricing is a huge issue in the current stage of the Web3 space, regardless of (or because of) ever-changing market sentiment. With the implementation of stable service pricing, Apillon provides the platform users with tools to build stellar Web3 products while allowing them to forecast their financial statements and cash flows for stable growth.
Paying for services with a highly volatile asset is both risky and cumbersome. It is mainly because, in Web3, highly volatile assets represent more an investment vehicle than a value transfer instrument.
The Apillon platform is designed to accept payments in either fiat money, stablecoins, or even volatile assets. As it cannot impact the utility of supported parachains' native tokens, the platform sustains a pricing model that resists their volatility and provides users with stable monthly pricing. This way, they can adequately plan the resources spent on Web3 projects.
While Apillon’s pricing model can buffer normal average asset volatility of supported services, it is not designed to withstand cases of dramatic or black swan events on the market.
The Apillon platform and its pricing is built using the following approaches:
The Apillon MVP supports a freemium model to bring the above points into equilibrium. With stably priced services and unified payment, it further helps Apillon's business model to stand the test of the crypto market.
',33),s=[n];function r(l,c){return i(),t("div",null,s)}const d=e(o,[["render",r],["__file","3-why-apillon.html.vue"]]);export{d as default}; diff --git a/assets/4-how-does-apillon-work.html-9f4211d2.js b/assets/4-how-does-apillon-work.html-9f4211d2.js new file mode 100644 index 00000000..d6457a59 --- /dev/null +++ b/assets/4-how-does-apillon-work.html-9f4211d2.js @@ -0,0 +1 @@ +import{_ as r,r as i,o as s,c as l,b as e,e as o,d as t,a as n}from"./app-270cc6ec.js";const d={},h=n('Apillon abstracts the technological complexity behind the Polkadot network and its parachains into a suite of development tools empowering developers to upgrade to Web3 easily.
The platform gathers and delivers Web3 functionalities powered by Polkadot parachains to developers through straightforward API access, and provides support with ample SDKs and complete documentation for each service.
Apillon's adaptability and interoperability allow builders to seamlessly merge use cases into single products offering different distributed services, serving their audiences more comprehensively than they could with specialized products.
Thanks to easy integration into any development framework, the Apillon APIs introduce a new way of Web3 development that significantly simplifies the go-to-market product evolution.
Initially, the Apillon platform will incorporate APIs to the following Polkadot parachains:
',8),p={href:"https://moonbeam.network/",target:"_blank",rel:"noopener noreferrer"},c={href:"https://www.kilt.io/",target:"_blank",rel:"noopener noreferrer"},u={href:"https://crust.network/",target:"_blank",rel:"noopener noreferrer"},f={href:"https://www.phala.network/",target:"_blank",rel:"noopener noreferrer"},m=n('In the future, more APIs will be added as new parachains earn their slot on Polkadot, and new Web3 services and features are released on the network.
Easing the integration of the Apillon platform and its APIs, ample SDKs enable faster and more streamlined utilization of Polkadot/Kusama parachains from the first click onwards.
Guiding developers’ way through the individual modules and dapp development process, Apillon documentation helps avoid bugs, mitigate operational issues, and create working products with a lower risk of error and less need for auditing.
With a real-time overview of back-end performance, developers gain valuable insights into the functioning of their Web3 applications and attached services, allowing them to improve on lagging features or tweak the product for better performance.
To help advance the adoption of Web3 products, Apillon's Analytics delivers clear insight into UI and transaction data, allowing developers to understand how their products are used, their best-performing features, and areas that need improvement.
',9);function w(g,k){const a=i("ExternalLinkIcon");return s(),l("div",null,[h,e("ul",null,[e("li",null,[e("a",p,[o("Moonbeam"),t(a)])]),e("li",null,[e("a",c,[o("KILT"),t(a)])]),e("li",null,[e("a",u,[o("Crust"),t(a)])]),e("li",null,[e("a",f,[o("Phala"),t(a)])])]),m])}const v=r(d,[["render",w],["__file","4-how-does-apillon-work.html.vue"]]);export{v as default}; diff --git a/assets/4-how-does-apillon-work.html-df2eb8bd.js b/assets/4-how-does-apillon-work.html-df2eb8bd.js new file mode 100644 index 00000000..cf65894c --- /dev/null +++ b/assets/4-how-does-apillon-work.html-df2eb8bd.js @@ -0,0 +1 @@ +const l=JSON.parse('{"key":"v-6bda2dde","path":"/about/4-how-does-apillon-work.html","title":"How does Apillon work?","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"APIs","slug":"apis","link":"#apis","children":[]},{"level":2,"title":"SDKs","slug":"sdks","link":"#sdks","children":[]},{"level":2,"title":"Documentation","slug":"documentation","link":"#documentation","children":[]},{"level":2,"title":"Monitoring","slug":"monitoring","link":"#monitoring","children":[]},{"level":2,"title":"Analytics","slug":"analytics","link":"#analytics","children":[]}],"git":{"updatedTime":1715328683000,"contributors":[{"name":"Damjan Dimitrov","email":"damjandimitrov1@gmail.com","commits":1}]},"filePathRelative":"about/4-how-does-apillon-work.md"}');export{l as data}; diff --git a/assets/4-nfts-api.html-3effba48.js b/assets/4-nfts-api.html-3effba48.js new file mode 100644 index 00000000..16ce7e91 --- /dev/null +++ b/assets/4-nfts-api.html-3effba48.js @@ -0,0 +1,258 @@ +import{_ as p,r as d,o as u,c as b,b as t,e,d as s,w as n,a as r}from"./app-270cc6ec.js";const k={},h=t("h1",{id:"nfts-api",tabindex:"-1"},[t("a",{class:"header-anchor",href:"#nfts-api","aria-hidden":"true"},"#"),e(" NFTs API")],-1),m={href:"https://docs.opensea.io/docs/metadata-standards",target:"_blank",rel:"noopener noreferrer"},v=t("h3",{id:"get-nft-collection",tabindex:"-1"},[t("a",{class:"header-anchor",href:"#get-nft-collection","aria-hidden":"true"},"#"),e(" Get NFT Collection")],-1),y=t("blockquote",null,[t("p",null,"Get NFT collection by UUID")],-1),f={class:"split_content"},g=r('Name | Description | Required |
---|---|---|
uuid | Unique key of collection. Key is displayed in developer dashboard. | true |
Code | Description |
---|---|
40300000 | Not allowed to access collection. |
50012009 | Collection does not exist. |
Name | Type | Description |
---|---|---|
createTime | DateTime | Collection create time. |
updateTime | DateTime | Collection last update time. |
collectionType | number | Type of smart contract to use for collection. Available types are described here. |
collectionUuid | string | Unique key of a collection. |
symbol | string | NFT collection symbol (usually 3-4 characters long). |
name | string | NFT collection name. |
description | string | NFT collection description. |
maxSupply | number | Maximal number of NFTs ever in existence (0 stands for unlimited). |
bucketUuid | string | UUID of the bucket where metadata is stored. |
baseUri | string | Base URI for collection metadata (token id and file extension is appended to it). |
baseExtension | string | File extension that is auto appended after token id to form a full URL. |
isSoulbound | boolean | Soul bound tokens are NFTs that are bounded to wallet and not transferable. |
isRevokable | boolean | For revocable collection owner can destroy NFTs at any time. |
isAutoIncrement | boolean | If set to false, enables minting NFTs with a custom token ID |
royaltiesFees | number | Percentage (between 0 and 100) of each NFT sale sent to wallet specified under royalties address. |
royaltiesAddress | string | Address where royalties are sent to. |
collectionStatus | number | Apillon internal/database collection status. |
contractAddress | string | Smart address of contract for deployed collection. |
transactionHash | string | Deployment transaction hash/id. |
deployerAddress | string | Wallet address of deployer. |
chain | number | Blockchain id on which you want to release your collection. |
drop | boolean | Determines if collection is mintable by public. |
dropStart | number | UNIX timestamp which determines public mint opening date and time. |
dropPrice | number | Price of NFT at mint stage in token that is used on chain . |
dropReserve | number | Amount of NFTs reserved by owner. |
Number | Description |
---|---|
0 | Generic NFT collection. |
1 | Nestable NFT collection. |
Number | Description |
---|---|
0 | Collection was created. |
1 | Deploying collection was initiated. |
2 | Collection is being deployed. |
3 | Collection was deployed successfully. |
4 | Collection was transferred successfully. |
5 | Failed deploying collection. |
',6),F={class:"split_content"},C={class:"split_side"},R=t("h4",{id:"query-parameters",tabindex:"-1"},[t("a",{class:"header-anchor",href:"#query-parameters","aria-hidden":"true"},"#"),e(" Query parameters")],-1),w=r('List NFT collections. Items are paginated and can be filtered and ordered through query parameters.
Name | Description | Required |
---|---|---|
collectionStatus | Collection status. Find available statuses here. | false |
Response is a list of items described under Response Fields above.
',3),A={class:"split_side"},D=t("div",{class:"language-bash line-numbers-mode","data-ext":"sh"},[t("pre",{class:"language-bash"},[t("code",null,[t("span",{class:"token function"},"curl"),e(),t("span",{class:"token parameter variable"},"--location"),e(),t("span",{class:"token string"},"'https://api.apillon.io/nfts/collections'"),e(),t("span",{class:"token punctuation"},"\\"),e(` +`),t("span",{class:"token parameter variable"},"--header"),e(),t("span",{class:"token string"},"'Authorization: Basic :credentials'"),e(` +`)])]),t("div",{class:"line-numbers","aria-hidden":"true"},[t("div",{class:"line-number"}),t("div",{class:"line-number"})])],-1),U=t("div",{class:"language-json line-numbers-mode","data-ext":"json"},[t("pre",{class:"language-json"},[t("code",null,[t("span",{class:"token punctuation"},"{"),e(` + `),t("span",{class:"token property"},'"id"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"b5935c73-204d-4365-9f9a-6a1792adab5b"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"status"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"200"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"data"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token punctuation"},"{"),e(` + `),t("span",{class:"token property"},'"items"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token punctuation"},"["),e(` + `),t("span",{class:"token punctuation"},"{"),e(` + `),t("span",{class:"token property"},'"createTime"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"2023-06-13T10:15:58.000Z"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"updateTime"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"2023-06-13T10:15:58.000Z"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"collectionType"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"1"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"collectionUuid"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"d6355fd3-640d-4803-a4d9-79d875abcb5a"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"symbol"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"NFT"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"name"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"NFT Collection"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"description"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"NFT Collection Description"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"maxSupply"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"1000"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"bucketUuid"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"a9425ff7-4802-4a38-b771-84a790112c30"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"baseUri"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"https://ipfs.apillon.io/metadata/"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"baseExtension"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'".json"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"isSoulbound"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token boolean"},"false"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"isRevokable"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token boolean"},"true"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"royaltiesFees"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"0.1"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"royaltiesAddress"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"0x4156edbafc5091507de2dd2a53ded551a346f83b"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"collectionStatus"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"0"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"contractAddress"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"0x452101C96A1Cf2cBDfa5BB5353e4a7F235241557"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"transactionHash"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"0x6b97424de3367cd0335b08265787b83053b62bee2d1c8bec1f776936bea4fb26"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"deployerAddress"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"0x4156edbafc5091507de2dd2a53ded551a346f83b"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"chain"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"1287"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"drop"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token boolean"},"true"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"dropStart"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"1687251003"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"dropPrice"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"0.1"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"dropReserve"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"5"),e(` + `),t("span",{class:"token punctuation"},"}"),e(` + `),t("span",{class:"token punctuation"},"]"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"total"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"1"),e(` + `),t("span",{class:"token punctuation"},"}"),e(` +`),t("span",{class:"token punctuation"},"}"),e(` +`)])]),t("div",{class:"line-numbers","aria-hidden":"true"},[t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"})])],-1),S=t("h3",{id:"list-collection-transactions",tabindex:"-1"},[t("a",{class:"header-anchor",href:"#list-collection-transactions","aria-hidden":"true"},"#"),e(" List Collection Transactions")],-1),B=t("blockquote",null,[t("p",null,"List NFT collections. Items are paginated and can be filtered and ordered through query parameters.")],-1),q={class:"split_content"},j={class:"split_side"},P=r('Name | Description | Required |
---|---|---|
uuid | Unique key of collection. Key is displayed in developer dashboard. | true |
Name | Description | Required |
---|---|---|
transactionStatus | Transaction status. | false |
transactionType | Transaction type. | false |
Name | Type | Description |
---|---|---|
chainId | number | Blockchain id on which you want to release your collection. |
transactionType | number | Transaction type. |
transactionStatus | number | Transaction status |
transactionHash | number | Transaction hash/id. |
updateTime | DateTime | Transaction last update time. |
createTime | DateTime | Transaction create time. |
Number | Description |
---|---|
1 | Deploy Contract |
2 | Transfer Contract Ownership |
3 | Mint NFT |
4 | Set Collection Base URI |
5 | Burn NFT |
6 | Nest mint NFT |
Number | Description |
---|---|
1 | Pending |
2 | Confirmed |
3 | Failed |
4 | Error |
API endpoint that creates NFT collection and deploys it on selected network.
An NFT Collection can be created with a few features/functionalities:
2 types of collections are supported:
',9),K={href:"https://evm.rmrk.app/general-overview/rmrk-legos/nestable",target:"_blank",rel:"noopener noreferrer"},Z=t("p",null,"Additionally, 2 chain types/environments are supported: EVM and Substrate.",-1),z=t("h4",{id:"create-substrate-nft-collection",tabindex:"-1"},[t("a",{class:"header-anchor",href:"#create-substrate-nft-collection","aria-hidden":"true"},"#"),e(" Create Substrate NFT Collection")],-1),H=t("br",null,null,-1),X={class:"split_content"},O=r('Name | Type | Description | Required |
---|---|---|---|
collectionType | number | Type of smart contract to use when deploying collection (1 for generic, 2 for nestable). | true |
chain | number | Blockchain ID on which you want to release your collection. Options: (8 - Astar) | true |
symbol | string | NFT collection symbol (usually 3-4 characters long). | true |
name | string | NFT collection name. | true |
description | string | NFT collection description. | false |
maxSupply | number | Maximal number of NFTs ever in existence (0 stands for unlimited). | true |
baseUri | string | Base URI for collection metadata (token id and file extension is appended to it). | true |
baseExtension | string | File extension that is auto appended after token id to form a full URL. | true |
royaltiesAddress | string | Address where royalties are sent to. | true |
royaltiesFees | number | Percentage of royalties earned per each NFT trade. | true |
drop | boolean | Determines if collection is mintable by public. | true |
dropStart* | number | UNIX timestamp (in seconds) which determines public mint opening date and time. | true |
dropPrice* | number | Price of NFT at mint stage. | true |
dropReserve* | number | Amount of NFTs reserved by owner. | true |
Notes:
*dropStart
, dropPrice
and dropReserve
are only used if drop
is set to boolean true
.
Beside validation errors (with 422 http status code) these are the error codes may be returned:
Code | Description |
---|---|
40012002 | Collection quota reached |
50012003 | Failed deploying NFT contract on chain. |
50012010 | Failed to create bucket for storing metadata. |
Response payload is described under Response Fields above.
Name | Type | Description | Required |
---|---|---|---|
chain | number | Blockchain ID on which you want to release your collection. Options: (1284 - Moonbeam, 1287 - Moonbase, 592 - Astar) | true |
isRevokable | boolean | For revocable collection owner can destroy NFTs at any time. (default: false) | true |
isSoulbound | boolean | Soul bound tokens are NFTs that are bound to wallet and not transferable. (default: false) | true |
isAutoIncrement | boolean | If set to false, enables minting NFTs with a custom token ID, otherwise defaults to isAutoIncrement = true | false |
Beside validation errors (with 422 http status code) these are the error codes may be returned:
Code | Description |
---|---|
40012002 | Collection quota reached |
50012003 | Failed deploying NFT contract on chain. |
50012010 | Failed to create bucket for storing metadata. |
Response payload is described under Response Fields above.
',6),nt={class:"split_side"},at=t("div",{class:"language-bash line-numbers-mode","data-ext":"sh"},[t("pre",{class:"language-bash"},[t("code",null,[t("span",{class:"token function"},"curl"),e(),t("span",{class:"token parameter variable"},"--location"),e(),t("span",{class:"token string"},"'https://api.apillon.io/nfts/collections/evm'"),e(),t("span",{class:"token punctuation"},"\\"),e(` +`),t("span",{class:"token parameter variable"},"--header"),e(),t("span",{class:"token string"},"'Content-Type: application/json'"),e(),t("span",{class:"token punctuation"},"\\"),e(` +`),t("span",{class:"token parameter variable"},"--header"),e(),t("span",{class:"token string"},"'Authorization: Basic :credentials'"),e(),t("span",{class:"token punctuation"},"\\"),e(` +`),t("span",{class:"token parameter variable"},"--data"),e(),t("span",{class:"token string"},`'{ + "collectionType": 1, + "chain": 1287, + "symbol": "NFT", + "name": "NFT Collection", + "description": "NFT Collection description", + "maxSupply": 1000, + "baseUri": "https://ipfs.apillon.io/metadata/", + "baseExtension": "json", + "royaltiesAddress": "0x4156edbafc5091507de2dd2a53ded551a346f83b", + "royaltiesFees": 10, + "drop": true, + "dropStart": 1687251003, + "dropReserve": 5, + "dropPrice": 0.1, + "isRevokable": true, + "isSoulbound": true +}'`),e(` +`)])]),t("div",{class:"line-numbers","aria-hidden":"true"},[t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"})])],-1),ot=t("div",{class:"language-json line-numbers-mode","data-ext":"json"},[t("pre",{class:"language-json"},[t("code",null,[t("span",{class:"token punctuation"},"{"),e(` + `),t("span",{class:"token property"},'"id"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"b5935c73-204d-4365-9f9a-6a1792adab5b"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"status"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"200"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"data"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token punctuation"},"{"),e(` + `),t("span",{class:"token property"},'"createTime"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"2023-06-13T10:15:58.000Z"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"updateTime"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"2023-06-13T10:15:58.000Z"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"chain"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"1287"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"collectionType"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"1"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"collectionUuid"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"d6355fd3-640d-4803-a4d9-79d875abcb5a"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"symbol"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"NFT"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"name"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"NFT Collection"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"description"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"NFT Collection Description"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"maxSupply"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"1000"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"bucketUuid"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"a9425ff7-4802-4a38-b771-84a790112c30"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"baseUri"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"https://ipfs.apillon.io/metadata/"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"baseExtension"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'".json"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"isSoulbound"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token boolean"},"false"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"isRevokable"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token boolean"},"false"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"royaltiesFees"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"0.1"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"royaltiesAddress"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"0x4156edbafc5091507de2dd2a53ded551a346f83b"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"collectionStatus"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"0"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"contractAddress"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"0x452101C96A1Cf2cBDfa5BB5353e4a7F235241557"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"transactionHash"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"0x6b97424de3367cd0335b08265787b83053b62bee2d1c8bec1f776936bea4fb26"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"deployerAddress"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token string"},'"0x4156edbafc5091507de2dd2a53ded551a346f83b"'),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"drop"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token boolean"},"true"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"dropStart"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"1687251003"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"dropPrice"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"0.1"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"dropReserve"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token number"},"5"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"isRevokable"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token boolean"},"true"),t("span",{class:"token punctuation"},","),e(` + `),t("span",{class:"token property"},'"isSoulbound"'),t("span",{class:"token operator"},":"),e(),t("span",{class:"token boolean"},"true"),e(` + `),t("span",{class:"token punctuation"},"}"),e(` +`),t("span",{class:"token punctuation"},"}"),e(` +`)])]),t("div",{class:"line-numbers","aria-hidden":"true"},[t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"}),t("div",{class:"line-number"})])],-1),rt=t("h3",{id:"transfer-collection",tabindex:"-1"},[t("a",{class:"header-anchor",href:"#transfer-collection","aria-hidden":"true"},"#"),e(" Transfer Collection")],-1),lt=t("blockquote",null,[t("p",null,"Transfer collection ownership from a wallet owned by caller to a new wallet address.")],-1),dt={class:"split_content"},ct=r('Name | Description | Required |
---|---|---|
uuid | Unique key of collection. Key is displayed in developer dashboard. | true |
Name | Type | Description | Required |
---|---|---|---|
address | string | Wallet address of a new owner. | true |
Beside validation errors (with 422 http status code) these are the error codes may be returned:
Code | Description |
---|---|
40012003 | Contract can't be transferred to wallet address that already owns this collection. |
40012004 | Transfer transaction already exists. |
40300000 | Not allowed to access collection |
50012002 | Collection doesn't exist, wasn't deployed or was already transferred. |
50012004 | Collection transfer failed. |
Response payload is described under Response Fields above.
Name | Description | Required |
---|---|---|
uuid | Unique key of collection. Key is displayed in developer dashboard. | true |
Name | Type | Description | Required |
---|---|---|---|
receivingAddress | string | Wallet address of NFT receiver. | true |
quantity | number | Number of NFTs to mint. | true |
Beside validation errors (with 422 http status code) these are the error codes may be returned:
Code | Description |
---|---|
40300000 | Not allowed to access collection. |
50012002 | Collection doesn't exist, wasn't deployed or was already transferred. |
50012005 | Error minting NFT. |
50012007 | Total number of minted NFTs would exceed max supply for this collection. |
50012008 | All of the reserved NFTs were already minted. |
Field | Type | Description |
---|---|---|
success | boolean | Status of action. |
Name | Description | Required |
---|---|---|
uuid | Unique key of (child) collection we are minting. | true |
Name | Type | Description | Required |
---|---|---|---|
parentCollectionUuid | string | Collection UUID of NFT receiving nest-minted NFT. | true |
parentNftId | number | Token id of NFT receiving nest-minted NFT. | true |
quantity | number | Number of NFTs to nest-mint. | true |
Beside validation errors (with 422 http status code) these are the error codes may be returned:
Code | Description |
---|---|
40300000 | Not allowed to access collection. |
50012002 | Collection doesn't exist, wasn't deployed or was already transferred. |
50012007 | Total number of minted NFTs would exceed max supply for this collection. |
50012008 | All of the reserved NFTs were already minted. |
50012013 | Parrent collection doesn't support nesting. |
50012014 | Parrent and child collection chain missmatch. |
Field | Type | Description |
---|---|---|
success | boolean | Status of action. |
Name | Description | Required |
---|---|---|
uuid | Unique key of collection. Key is displayed in developer dashboard. | true |
Name | Type | Description | Required |
---|---|---|---|
tokenId | number | Non fungible token id that we are burning. | true |
Beside validation errors (with 422 http status code) these are the error codes may be returned:
Code | Description |
---|---|
40300000 | Not allowed to access collection. |
50012002 | Collection doesn't exist, wasn't deployed or was already transferred. |
50012012 | Burning NFT failed. |
Field | Type | Description |
---|---|---|
success | boolean | Status of action. |
npm install @apillon/sdk
+
import { Hosting } from '@apillon/sdk';
+
+const hosting = new Hosting({
+ key: '',
+ secret: '',
+});
+
Apillon SDK consists of different modules depending on which service you want to use. All modules require the same initial config of key
and secret
shown above in Hosting
module example.
Alternatively, you can populate the APILLON_API_KEY
and APILLON_API_SECRET
environment variables.
View each individual module examples in the sections below.
import {
+ DeployToEnvironment,
+ DeploymentStatus,
+ Hosting,
+ LogLevel,
+} from '@apillon/sdk';
+import * as fs from 'fs';
+
+const hosting = new Hosting({
+ key: 'yourApiKey',
+ secret: 'yourApiSecret',
+ logLevel: LogLevel.VERBOSE,
+});
+
+// list all websites
+await hosting.listWebsites({ orderBy: 'createTime' });
+
+// create an instance of a website via uuid
+const webpage1 = hosting.website('uuid');
+
+// gets website information
+await webpage1.get();
+
+// Upload files from local folder
+await webpage1.uploadFromFolder('./public');
+// Or alternatively, send file buffers as upload parameters
+const htmlBuffer = fs.readFileSync('./public/index.html');
+await webpage1.uploadFiles(
+ [
+ {
+ fileName: 'index.html',
+ contentType: 'text/html',
+ content: htmlBuffer,
+ },
+ ]
+);
+
+// deploys uploaded files to staging environment
+await webpage1.deploy(DeployToEnvironment.TO_STAGING);
+
+// lists all deployments of a website
+await webpage1.listDeployments();
+
+// gets a specific deployment
+const deployment = await webpage1
+ .deployment('3e0c66ea-317d-4e1f-bcd9-38026c3ea1ee')
+ .get();
+
+// checks if deployment was successful
+if (deployment.deploymentStatus === DeploymentStatus.SUCCESSFUL) {
+ // done
+}
+
Storage module encapsulates functionalities for Storage service available on Apillon dashboard.
`,4),H={href:"https://sdk-docs.apillon.io/classes/Storage.html",target:"_blank",rel:"noopener noreferrer"},q=e(`import { Storage, LogLevel, FileStatus } from '@apillon/sdk';
+import * as fs from 'fs';
+
+const storage = new Storage({
+ key: 'yourApiKey',
+ secret: 'yourApiSecret',
+ logLevel: LogLevel.VERBOSE,
+});
+
+// list buckets
+await storage.listBuckets({ limit: 5 });
+
+// create and instance of a bucket directly through uuid
+const bucket = storage.bucket('uuid');
+
+// Upload files from local folder
+await bucket.uploadFromFolder('./my-folder/files/');
+// Or alternatively, send file buffers as upload parameters
+const pdfBuffer = fs.readFileSync('./my-folder/files/document.pdf');
+await bucket.uploadFiles(
+ [
+ {
+ fileName: 'document.pdf',
+ contentType: 'application/pdf',
+ content: pdfBuffer,
+ },
+ ],
+ // Upload the files in a new subdirectory in the bucket instead of in the root of the bucket
+ { wrapWithDirectory: true, directoryPath: 'main/documents' }
+);
+
+// list objects (files, folders) in a bucket
+await bucket.listObjects({
+ directoryUuid: 'eaff2672-3012-46fb-9278-5efacc6cb616',
+ markedForDeletion: false,
+ limit: 5,
+});
+
+// list all files in a bucket no matter if they are in a folder or not
+await bucket.listFiles({ fileStatus: FileStatus.UPLOADED });
+
+// generate an IPFS link for a CID
+const cid = 'bafybeigjhyc2tpvqfqsuvf3byo4e4a4v6spi6jk4qqvvtlpca6rsaf2cqi';
+const link = await storage.generateIpfsLink(cid);
+
+// gets a specific file in a bucket directly through uuid
+const file = await bucket.file('2195521d-15cc-4f6e-abf2-13866f9c6e03').get();
+
+// deletes a file via uuid
+await bucket.file('2195521d-15cc-4f6e-abf2-13866f9c6e03').delete();
+// deletes a directory via uuid
+await bucket.directory('eddc52cf-92d2-436e-b6de-42d7cad621c3').delete();
+
The Storage module additionally contains methods for manipulating IPNS records for a specific storage any.
`,4),M={href:"https://sdk-docs.apillon.io/classes/Ipns.html",target:"_blank",rel:"noopener noreferrer"},V=e(`import { Storage, LogLevel } from '@apillon/sdk';
+
+const storage = new Storage({
+ key: 'yourApiKey',
+ secret: 'yourApiSecret',
+ logLevel: LogLevel.VERBOSE,
+});
+
+// create and instance of a bucket directly through uuid
+const bucket = storage.bucket('uuid');
+// list all existing IPNS records
+const ipnsNames = await bucket.listIpnsNames({ ipnsName: 'Images IPNS' });
+// create a new IPNS record
+const newIpns = await bucket.createIpns({
+ name: 'Music IPNS',
+ description: 'IPNS for my music files',
+ cid: 'QmS5NL2Rc6SCjFx7pvZHdTD8WGWjDt25WQskC7DsNKAatW',
+});
+// Get an IPNS record's details by UUID
+const ipns = await bucket.ipns('ipns_uuid').get();
+// Publish an IPNS record to point to a given CID
+await ipns.publish('QmajaeC15ZpcnjBpX4ARRBU127fpcZ2svYEfEBhFRkRZbN');
+// delete an IPNS record from the bucket
+await ipns.delete();
+
NFT module encapsulates functionalities for NFT service available on Apillon dashboard.
`,3),W={href:"https://sdk-docs.apillon.io/classes/Nft.html",target:"_blank",rel:"noopener noreferrer"},j=e(`Warning When you transfer ownership of the collection to another account Apillon will lose the ability to perform actions in your name (mint, burn, etc.). Before you transfer ownership make sure you do not need those functionalities via Apillon anymore.
import {
+ CollectionType,
+ EvmChain,
+ LogLevel,
+ Nft,
+ TransactionStatus,
+} from '@apillon/sdk';
+
+const nft = new Nft({
+ key: 'yourApiKey',
+ secret: 'yourApiSecret',
+ logLevel: LogLevel.VERBOSE,
+});
+
+// create a new collection
+let collection = await nft.create({
+ collectionType: CollectionType.GENERIC,
+ chain: EvmChain.MOONBEAM,
+ name: 'SpaceExplorers',
+ symbol: 'SE',
+ description: 'A collection of unique space exploration NFTs.',
+ baseUri: 'https://moonbeamnfts.com/collections/spaceexplorers/',
+ baseExtension: 'json',
+ // If you omit the maxSupply parameter, the max supply will be unlimited
+ maxSupply: 1000,
+ isRevokable: false,
+ isSoulbound: false,
+ royaltiesAddress: '0x1234567890abcdef',
+ royaltiesFees: 5,
+ drop: true,
+ dropStart: 1679875200,
+ dropPrice: 0.05,
+ dropReserve: 100,
+});
+// or create a substrate collection
+const substrateCollection = await nft.createSubstrate({
+ collectionType: CollectionType.GENERIC,
+ chain: SubstrateChain.ASTAR,
+ name: 'SpaceExplorers',
+ symbol: 'SE',
+ ...
+});
+
+// check if collection is deployed - available on chain
+if (collection.collectionStatus == CollectionStatus.DEPLOYED) {
+ console.log('Collection deployed: ', collection.transactionHash);
+}
+
+// search through collections
+await nft.listCollections({ search: 'My NFT' });
+
+// create and instance of collection directly through uuid
+collection = await nft.collection('uuid').get();
+
+// mint a new nft in the collection
+await collection.mint({
+ receivingAddress: '0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD',
+ quantity: 1,
+});
+
+// nest mint a new nft if collection type is NESTABLE
+await collection.nestMint(collection.uuid, 1, 1);
+
+// burn/destroy a specific NFT by its ID if collection is set as revokable
+await collection.burn('1');
+
+// list confirmed transactions on a collection
+await collection.listTransactions({
+ transactionStatus: TransactionStatus.CONFIRMED,
+});
+
+// transfer ownership of a collection away from apillon platform to an address
+// NOTE that this will disable the ability to mint/burn etc. from the SDK/API since only the owner
+// has this ability
+await collection.transferOwnership(
+ '0x5BA8B0c24bA5307b67E619ad500a635204F73bF1'
+);
+
Identity module encapsulates functionalities for validating EVM and Polkadot wallet signatures, as well as fetching Polkadot Identity data for any wallet.
`,5),G={href:"https://sdk-docs.apillon.io/classes/Identity.html",target:"_blank",rel:"noopener noreferrer"},Y=e(`import { Identity, LogLevel } from '@apillon/sdk';
+
+// Note: for signature-related methods API config is not required
+const identity = new Identity({
+ key: 'yourApiKey',
+ secret: 'yourApiSecret',
+ logLevel: LogLevel.VERBOSE,
+});
+
+// obtain on-chain identity data for a Polkadot wallet
+const { polkadot, subsocial } = await identity.getWalletIdentity(address);
+
+async function validateEvmWalletSignature() {
+ // generate a custom message to be signed by the user's wallet
+ const { message, timestamp } = await identity.generateSigningMessage(
+ 'Custom display message here',
+ );
+
+ // alternatively, you can generate you own signing message with timestamp like this:
+ // const timestamp = new Date().getTime();
+ // const message = 'Message from my Dapp';
+ // const signingMessage = \`\${message}\\n\${timestamp}\`;
+
+ const walletAddress = '0xa79bg13g2...';
+
+ // validate an EVM wallet's signature for a given message
+ const { isValid, address } = await identity.validateEvmWalletSignature({
+ message,
+ signature, // signature obtained from the user's wallet by the client app
+ walletAddress,
+ /*
+ * optional - check signature time validity by providing a timestamp
+ * which indicates when the signature was generated
+ */
+ timestamp,
+ // additionally, specify for how many minutes the timestamp is valid
+ signatureValidityMinutes: 15,
+ });
+
+ console.log(isValid); // true
+ console.log(address.toLowerCase() === walletAddress.toLowerCase()); // true
+}
+
+async function validatePolkadotWalletSignature() {
+ // If you wish to generate the message yourself and validate the timestamp,
+ // use a signing message as shown below:
+ const timestamp = new Date().getTime();
+ const message = 'Message from my Dapp';
+ const signingMessage = \`\${message}\\n\${timestamp}\`;
+
+ // validate a Polkadot wallet's signature for a given signing message
+ const { isValid } = await identity.validatePolkadotWalletSignature({
+ message: signingMessage,
+ signature, // signature obtained from the user's wallet by the client app
+ walletAddress: '5HqHQDGcHqS...',
+ timestamp,
+ signatureValidityMinutes: 5,
+ });
+}
+
+
The Computing module provides functionalities for managing computing contracts, including creating contracts, listing contracts, and interacting with specific contracts for operations like encryption and ownership transfer.
import { Computing } from '@apillon/sdk';
+
+const computing = new Computing({
+ key: 'yourApiKey',
+ secret: 'yourApiSecret',
+});
+
+// List all computing contracts
+const contracts = await computing.listContracts();
+
+// Create a new computing contract
+const newContract = await computing.createContract({
+ name: 'New Contract',
+ description: 'Description of the new contract',
+ bucket_uuid,
+ contractData: {
+ nftContractAddress: '0xabc...',
+ nftChainRpcUrl: ChainRpcUrl.ASTAR,
+ },
+});
+
+// Interact with a specific computing contract
+const contract = computing.contract(newContract.uuid);
+
+// Get details of the contract
+const contractDetails = await contract.get();
+
+// List transactions of the contract
+const transactions = await contract.listTransactions();
+
+// Encrypt a file and upload it to the associated bucket
+const encryptionResult = await contract.encryptFile({
+ fileName: 'example.txt',
+ content: Buffer.from('Hello, world!'),
+ nftId: 1, // NFT ID used for decryption authentication
+});
+
+// Transfer ownership of the contract
+const newOwnerAddress = '0xNewOwnerAddress';
+const successResult = await contract.transferOwnership(newOwnerAddress);
+console.log(
+ \`Ownership transfer was \${successResult ? 'successful' : 'unsuccessful'}.\`,
+);
+
The Social module provides functionalities for managing social hubs and channels within the Apillon platform. This includes creating, listing, and interacting with hubs and channels. In the background it utilizes Grill.chat, a mobile-friendly, anonymous chat application powered by Subsocial.
import { Social } from '@apillon/sdk';
+
+const social = new Social({ key: 'yourApiKey', secret: 'yourApiSecret' });
+// Create a new hub
+const hub = await social.createHub({
+ name: 'Apillon Hub',
+ about: 'Hub for Apillon channels',
+ tags: 'apillon,web3,build',
+});
+
+// Get a specific hub by UUID
+const hubDetails = await social.hub(hub.uuid).get();
+// List all Hubs
+const hubs = await social.listHubs();
+
+// Create a new channel within a hub
+const channel = await social.createChannel({
+ title: 'Web3 Channel',
+ body: "Let's discuss Web3",
+ tags: 'web3,crypto',
+ hubUuid: hub.uuid,
+});
+
+// Get a specific channel by UUID
+const channelDetails = await social.channel(channel.uuid).get();
+// List all channels within a Hub
+const channels = await social.listChannels({ hubUuid: hub.uuid });
+
When building on Web3, be it within the Polkadot ecosystem or in general, developers face challenges that still lack proper addressing.
With these obstacles in mind, the process of building a Web3 product from scratch soon becomes a complex, time-consuming, and potentially even risky business.
To overcome them, developers would need to:
Using Apillon, on the other hand, the process of developing a Web3 product is radically different.
Example #1: Building on KILT Protocol*
To build a simple Web3 app utilizing KILT Protocol for user authentication from scratch, a developer needs to do (at least) the following:
Using the Apillon platform, on the other hand, a developer simply calls a function (e. g., createUserWithEmailAndPassword
) from the Apillon SDK and sends the required parameters. The function creates a fully working user DID in the back end.
Example #2: Building on Crust*
To build a simple Web3 app utilizing Crust for file storage from scratch, a developer needs to do (at least) the following:
Using the Apillon platform, on the other hand, a developer simply calls the getStorage()
SDK function and moves the files to a decentralized, pinned service provided by Crust and IPFS.
In both cases, the resources spent on building a functional Web3 application with Apillon are significantly reduced, and the product’s go-to-market trajectory much shorter and streamlined.
*Disclaimer: These examples are technically highly simplified to illustrate the problematic context of building a Web3 product to the general public, whereas in technical reality, the process is much more complex. Examples used do not intend to imply in any way that either KILT or Crust are challenging to use but merely to show that these processes require serious work and introduce friction in cases where developers utilize several parachains to build a single solution.
',20),a=[l];function r(s,d){return i(),t("div",null,a)}const h=e(n,[["render",r],["__file","5-developing-web3-with-apillon.html.vue"]]);export{h as default}; diff --git a/assets/5-developing-web3-with-apillon.html-eb7334b6.js b/assets/5-developing-web3-with-apillon.html-eb7334b6.js new file mode 100644 index 00000000..dd3c1646 --- /dev/null +++ b/assets/5-developing-web3-with-apillon.html-eb7334b6.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-ac15b5a4","path":"/about/5-developing-web3-with-apillon.html","title":"Developing Web3 with Apillon","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Challenges of blockchain complexity","slug":"challenges-of-blockchain-complexity","link":"#challenges-of-blockchain-complexity","children":[]},{"level":2,"title":"Web3 Development with and without Apillon","slug":"web3-development-with-and-without-apillon","link":"#web3-development-with-and-without-apillon","children":[]}],"git":{"updatedTime":1715328683000,"contributors":[{"name":"Damjan Dimitrov","email":"damjandimitrov1@gmail.com","commits":1}]},"filePathRelative":"about/5-developing-web3-with-apillon.md"}');export{e as data}; diff --git a/assets/5-web3-authentication.html-99f98d12.js b/assets/5-web3-authentication.html-99f98d12.js new file mode 100644 index 00000000..830d858b --- /dev/null +++ b/assets/5-web3-authentication.html-99f98d12.js @@ -0,0 +1 @@ +import{_ as r,r as i,o as s,c as h,b as e,e as t,d as n,w as l,a as c}from"./app-270cc6ec.js";const d={},p=c('Apillon offers a method for users to integrate an alternative authentication approach using a decentralized identifier (DID) stored on the KILT parachain. KILT-powered decentralized identity includes a credential that comprises a user's email address, which can be attested by Apillon or SocialKYC. Any user can create their own decentralized identity, verify their email address through Apillon's OAuth protocol, and then use this decentralized account and credential to verify their identity on third-party platforms.
Email Verification: This is the initial step in the process of generating a decentralized identity, which also serves as part of the attestation process, as it attests ownership of the provided email address.
Account Generation: A KILT wallet address derived from a BIP39 mnemonic is created. This mnemonic serves as the master key for accessing the account. Users are prompted to store this mnemonic securely.
Identity Generation: This step typically takes one to five minutes. On the front end, the user signs the operation for DID creation with their Sporran wallet, which is then submitted to the blockchain. The result is a DID document. Once the process is complete, the DID document is queried from the blockchain and returned to the user. All querying occurs on the front end, ensuring that Apillon never gains access to the DID document or the generated mnemonic from the previous step. The user has the option to link their newly generated DID with their account, making it possible to retrieve a DID for an account and vice-versa.
Attestation: Attestation consists of two steps. The first is verifying a user's claim, which is the result of the initial email verification step. The second step involves creating a verifiable credential, signed by both Apillon and the claimer (using the authentication key derived from the generated mnemonic in the second step). This credential's root hash is then submitted to the blockchain. The combination of these steps is referred to as the attestation process, and the credential should be stored securely.
DID and Verifiable Credential Storage: The user is prompted to save the generated files. The generated credential can also be imported into the Sporran wallet for convenience and safekeeping.
Identity Revocation: The user has the option to request the revocation of their DID through Apillon's OAuth website.
The first step mirrors the registration process, where an email with a unique token is sent to the user.
A revocation operation for the DID document is issued to the blockchain, rendering the identity invalid. All associated verifiable credentials also become unusable as a result of this process. The wallet account generated in the second step of the registration process remains valid, and all tokens associated with this account address remain accessible and valid.
Credential Restore: The user can restore their previously generated credentials through Apillon's OAuth website in case they lose access to them. The first step is the same as the registration process and involves sending an email with a unique token. Upon following the verification email link, the user is redirected to a secure page, where the saved credential is returned to them.
Credential Verification: The verification process requires two parameters: a verifiable credential (which can be in JSON format or pasted as plain text in the text area) and the mnemonic passphrase used to sign the credential.
From the provided credential and mnemonic, a presentation is created. The presentation should only contain the user's email address, which is the single property requested by Apillon to complete the verification process. The verification process also checks whether the owner of the credential matches the address that signed the request. The presentation is considered valid if the integrity check mentioned above succeeds.
Alternatively, if a user has imported their previously generated credentials into the Sporran wallet, they can log in directly with Sporran without the need to provide the credentials in text or file format, or the mnemonic passphrase. This allows users to select an existing credential from their wallet and sign the request directly.
To install Apillon CLI run
npm install -g @apillon/cli
+
Afterwards you can use CLI with command
apillon <command> [options]
+
Alternately you don't need to install the Apillon CLI to use it. In that case run the desired command using npx
:
npx @apillon/cli <command> [options]
+
Note that when running without installation, you have to use
@apillon/cli
instead ofapillon
execution command.
--key <api key>
: Apillon API key (can be set via the APILLON_API_KEY
environment variable).--secret <api secret>
: Apillon API secret (can be set via the APILLON_API_SECRET
environment variable).--debug
: Output execution logs when running commands.-V
, --version
: Output the version number.You can use environment variables to set an API key, and an API secret:
APILLON_API_KEY
: Apillon API key.APILLON_API_SECRET
: Apillon API secret.If you have these variables set, you do not need to use the global options each time.
To display the help information for the CLI or a specific command, use the -h
or --help
option:
apillon -h
+apillon hosting -h
+npx @apillon/cli hosting deploy-website --help
+
For commands that return a list of results, for example apillon storage list-files
, or apillon hosting list-websites
, there are global list pagination options that are available to use:
--limit <integer>
: Page limit--order-by <string>
: Page order by (can be any property from the response data)--page <integer>
: Page number--search <string>
: Search by name or other object identifierThe Apillon CLI currently supports the following commands:
hosting list-websites
Lists all websites associated with your project.
Example
apillon hosting list-websites --search "My-Website" --limit 1
+
Example response
{
+ "items": [
+ {
+ "createTime": "2023-10-25T10:41:06.000Z",
+ "updateTime": "2023-10-26T12:41:41.000Z",
+ "uuid": "5b908779-3687-4592-a073-9bebbf86afe2",
+ "name": "My Website",
+ "description": "My own website",
+ "domain": "https://my-website.com",
+ "bucketUuid": "47251013-37c6-4b30-be2b-8583dea25c4c",
+ "ipnsStaging": "k2k4r8ob2rf35wbmhhtzbq6nd4lhwv...",
+ "ipnsProduction": "k2k4r8pple7phwm9azqgxshxdzy..."
+ },
+ ...
+ ],
+ "total": 3
+}
+
hosting get-website
Retrieves information about a specific website.
Options
--uuid <string>
: UUID of the website to get details for.Example
apillon hosting get-website --uuid "123e4567-e89b-12d3-a456-426655440000"
+
hosting deploy-website
Deploys a website from a local folder directly to Apillon hosting production environment.
Options
<file-path>
: Path to the folder containing your website files.--uuid <string>
: UUID of the website to upload files to.-p, --preview
: Deploy to staging environment instead.Example
apillon hosting deploy-website ./public_html --uuid "123e4567-e89b-12d3-a456-426655440000" -p
+
hosting upload
Uploads a local folder's contents to a website deployment bucket.
Options
<path>
: Path to the folder containing your website files.--uuid <string>
: UUID of the website to upload files to.Example
apillon hosting upload ./public_html --uuid "123e4567-e89b-12d3-a456-426655440000"
+
hosting start-deployment
Deploys a website to the specified environment, from files already uploaded to the hosting bucket.
Options
--uuid <string>
: UUID of the website to deploy.--env <integer>
: The environment to deploy to.Available choices:
TO_STAGING = 1
+STAGING_TO_PRODUCTION = 2
+DIRECTLY_TO_PRODUCTION = 3
+
Example
apillon hosting start-deployment --uuid "123e4567-e89b-12d3-a456-426655440000" --env 1
+
hosting list-deployments
Lists all deployments for a specific website.
Options
--uuid <string>
: UUID of the website to list deployments for.--status <integer>
: The status of the deployments (DeploymentStatus enum, optional).Available choices:
INITIATED = 0
+IN_PROGRESS = 1
+IN_REVIEW = 2
+APPROVED = 3
+SUCCESSFUL = 10
+FAILED = 100
+REJECTED = 101
+
--env <integer>
: The environment of the deployments (DeploymentStatus enum, optional).Available choices:
STAGING = 2
+PRODUCTION = 3
+
Example
apillon hosting list-deployments --uuid "58a16026-1356-405b-97f9-efcc9dfac1dd" --order-by createTime --desc true
+
Example response
{
+ "items": [
+ {
+ "createTime": "2023-11-14T12:09:20.000Z",
+ "updateTime": "2023-11-14T12:09:42.000Z",
+ "uuid": "9b677fe2-1bb1-44d9-8956-e7749452f02d",
+ "websiteUuid": "58a16026-1356-405b-97f9-efcc9dfac1dd",
+ "cid": "QmPPBMsFccJVaLwvdhSh3zMbfEvonxoNSBLVd1kWK34Nps",
+ "cidv1": "bafybeizpqaa5xb5r46d2voj35qtokhb3c3bekof...",
+ "environment": "DIRECTLY_TO_PRODUCTION",
+ "deploymentStatus": "SUCCESSFUL",
+ "size": 7162,
+ "number": 1
+ },
+ ...
+ ],
+ "total": 7
+}
+
hosting get-deployment
Retrieves information about a specific deployment.
Options
-w, --website-uuid <string>
: UUID of the website.-d, --deployment-uuid <string>
: UUID of the deploymentExample
apillon hosting get-deployment --website-uuid "123e4567-e89b-12d3-a456-426655440000" --deployment-uuid "987e6543-e21c-32f1-b123-426655441111"
+
storage list-buckets
Lists all storage buckets associated with your project.
Example
apillon storage list-buckets
+
Example response
{
+ "items": [
+ {
+ "createTime": "2023-11-15T09:56:53.000Z",
+ "updateTime": "2023-11-23T08:55:46.000Z",
+ "uuid": "91c57d55-e8e4-40b7-ad6a-81a82831bfb3",
+ "name": "My Storage Bucket",
+ "description": "For storing my images and videos",
+ "size": 23576
+ },
+ ...
+ ],
+ "total": 2
+}
+
storage list-objects
Retrieves objects (files and folders) from a specific bucket or bucket directory.
Options
-b, --bucket-uuid <string>
: UUID of the bucket to retrieve objects from.-d, --directory-uuid <string>
: UUID of the directory to retrieve objects from (optional, default root folder).--deleted
: Include objects deleted from the bucket.Example
apillon storage list-objects --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --directory-uuid "987e6543-e21c-32f1-b123-426655441111"
+
Example response
{
+ "items": [
+ {
+ "createTime": "2023-11-23T08:55:45.000Z",
+ "updateTime": "2023-11-23T08:55:46.000Z",
+ "uuid": "14a7a891-877c-41ac-900c-7382347e1e77",
+ "name": "index.html",
+ "CID": "bafybeidzrd7p5ddj67j2mud32cbnze2c7b2pvbhn...",
+ "status": "AVAILABLE_ON_IPFS_AND_REPLICATED",
+ "directoryUuid": null,
+ "type": "FILE",
+ "link": "https://ipfs.apillon.io/ipfs/bafybeidzrd7p5ddj67j...",
+ "path": null,
+ "bucketUuid": "91c57d55-e8e4-40b7-ad6a-81a82831bfb3"
+ },
+ ...
+ ],
+ "total": 16
+}
+
storage list-files
Retrieves files from a specific bucket.
Options
-b, --bucket-uuid <string>
: UUID of the bucket to retrieve files from.-s, --file-status <integer>
: Filter by file status (FileStatus enum, optional).Available choices:
UPLOAD_REQUEST_GENERATED = 1
+UPLOADED = 2
+AVAILABLE_ON_IPFS = 3
+AVAILABLE_ON_IPFS_AND_REPLICATED = 4
+
Example
apillon storage list-files --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" -s 2
+
Example response
{
+ "items": [
+ {
+ "createTime": "2023-11-15T09:58:04.000Z",
+ "updateTime": "2023-11-15T09:58:10.000Z",
+ "name": "style.css",
+ "CID": "bafybeidzrd7p5ddj67j2mud32cbnze2c7b2pvbhn...",
+ "status": "AVAILABLE_ON_IPFS_AND_REPLICATED",
+ "directoryUuid": null,
+ "type": "FILE",
+ "link": "https://ipfs.apillon.io/ipfs/bafybeidzrd7p...",
+ "path": null,
+ "bucketUuid": "91c57d55-e8e4-40b7-ad6a-81a82831bfb3"
+ },
+ ...
+ ],
+ "total": 10
+}
+
storage upload
Upload contents of a local folder to specified bucket.
Options
<folder-path>
: Path to the folder containing your files.-b, --bucket-uuid <string>
: UUID of the bucket to upload files to.-w, --wrap
: Wrap uploaded files to an IPFS directory-p, --path <string>
: Path to upload files to (e.g. main/subdir). Required when --wrap is supplied.--await
: await file CIDs to be resolvedExample
apillon storage upload ./my_folder --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --wrap --path "main/subdir"
+
storage get-file
Retrieves information about a specific file in a bucket.
Options
-b, --bucket-uuid <string>
: UUID of the bucket.-f, --file-uuid <string>
: UUID or CID of the file to retrieve.Example
apillon storage get-file --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --file-uuid "file_uuid_or_cid"
+
storage delete-file
Deletes a specific file from a bucket.
Options
-b, --bucket-uuid <string>
: UUID of the bucket.-f, --file-uuid <string>
: UUID or CID of the file to delete.Example
apillon storage delete-file --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --file-uuid "file_uuid_or_cid"
+
storage delete-directory
Delete a directory from a storage bucket.
Options
-b, --bucket-uuid <string>
: UUID of the bucket.-d, --directory-uuid <string>
: UUID of the directoru to delete.Example
apillon storage delete-directory --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --directory-uuid "2c84048c-49a1-4ed2-9e1e-8920806ae968"
+
storage ipns list
Lists all IPNS records for a specific bucket.
Options
-b, --bucket-uuid <uuid>
: UUID of the bucket.Example
apillon storage ipns list --bucket-uuid "123e4567-e89b-12d3-a456-426655440000"
+
Example response
{
+ "items": [
+ {
+ "createTime": "2023-12-06T12:29:09.000Z",
+ "updateTime": "2023-12-06T12:29:21.000Z",
+ "uuid": "80383a54-1d86-4761-a5e4-26a2fab474c9",
+ "name": "Images IPNS",
+ "description": "IPNS for images folder",
+ "ipnsName": "k2k4r8jp1jnlbe3qv...",
+ "ipnsValue": "/ipfs/QmUz4...",
+ "link": "https://ipfs.apillon.io/ipns/k2k4r8jp1jnlbe3qv...",
+ "bucketUuid": "a26184d7-acf5-4d6c-9195-465e3a7a5240"
+ },
+ {
+ "createTime": "2023-12-06T12:29:37.000Z",
+ "updateTime": "2023-12-06T12:29:52.000Z",
+ "uuid": "2045db5b-b347-4ea6-a4c0-4445e071180d",
+ "name": "JSON IPNS",
+ "description": "IPNS for metadata folder",
+ "ipnsName": "k2k4r8opkl3i2zq7bin8lis4...",
+ "ipnsValue": "/ipfs/QmUz5Z6RcMynfZWoC...",
+ "link": "https://ipfs.apillon.io/ipns/k2k4r8opkl3i2z...",
+ "bucketUuid": "a26184d7-acf5-4d6c-9195-465e3a7a5240"
+ }
+ ],
+ "total": 2
+}
+
storage ipns create
Creates a new IPNS record for a specific bucket.
Options
-b, --bucket-uuid <uuid>
: UUID of the bucket.-n, --name <name>
: Name of the IPNS record.-d, --description <description>
: Description of the IPNS record (optional).-c, --cid <cid>
: CID to which this IPNS name will point.Example
apillon storage ipns create --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --name "my-ipns-record" --cid "QmWX5CcNvnaVmgGBn4o82XW9uW1uLvsHQDdNrANrQeSdXm"
+
storage ipns get
Retrieves information about a specific IPNS record.
Options
-b, --bucket-uuid <uuid>
: UUID of the bucket.-i, --ipns-uuid <uuid>
: UUID of the IPNS record.Example
apillon storage ipns get --ipns-uuid "123e4567-e89b-12d3-a456-426655440000"
+
storage ipns publish
Publishes an IPNS record to IPFS and links it to a CID.
Options
-b, --bucket-uuid <uuid>
: UUID of the bucket.-i, --ipns-uuid <uuid>
: UUID of the IPNS record.-c, --cid <string>
: CID to which this IPNS name will point.Example
apillon storage ipns publish --ipns-uuid "123e4567-e89b-12d3-a456-426655440000" --cid "QmWX5CcNvnaVmgGBn4o82XW9uW1uLvsHQDdNrANrQeSdXm"
+
storage ipns delete
Deletes an IPNS record from a specific bucket.
Options
-b, --bucket-uuid <uuid>
: UUID of the bucket.-i, --ipns-uuid <uuid>
: UUID of the IPNS record.Example
apillon storage ipns delete --ipns-uuid "123e4567-e89b-12d3-a456-426655440000"
+
nfts list-collections
Lists all NFT collections owned by the project related to the API key.
Options
--status <integer>
: UUID of the collection to retrieve (CollectionStatus enum, optional).Available choices:
CREATED = 0
+DEPLOY_INITIATED = 1
+DEPLOYING = 2
+DEPLOYED = 3
+TRANSFERRED = 4
+FAILED = 5
+
Example
apillon nfts list-collections --status 3
+
Example response
{
+ "items": [
+ {
+ "createTime": "2023-11-20T10:21:12.000Z",
+ "updateTime": "2023-11-20T14:12:33.000Z",
+ "uuid": "2cda3a9b-01b1-4b5e-9709-7087129d55d0",
+ "symbol": "SE",
+ "name": "SpaceExplorers",
+ "description": "A collection of unique space exploration NFTs.",
+ "collectionType": "GENERIC",
+ "maxSupply": 1000,
+ "baseUri": "https://moonbeamnfts.com/collections/spaceexplorers/",
+ "baseExtension": ".json",
+ "isSoulbound": false,
+ "isRevokable": false,
+ "drop": false,
+ "dropPrice": 0.05,
+ "dropStart": 1679875200,
+ "dropReserve": 100,
+ "royaltiesFees": 5,
+ "royaltiesAddress": "0xaz5Bh6E56c5d3B58c944542de2bF18E7F65eED82",
+ "collectionStatus": "TRANSFERRED",
+ "contractAddress": "0x4e22162A6d0c91a088Cb57A72aB976ccA2A96B25",
+ "transactionHash": null,
+ "deployerAddress": "0xba015fgc6d80378a9a95f1687e9960857593983b",
+ "chain": "MOONBASE"
+ }
+ ],
+ "total": 1
+}
+
nfts get-collection
Retrieves information about a specific NFT collection.
Options
--uuid <collection-uuid>
: UUID of the collection to retrieve.Example
apillon nfts get-collection --uuid "123e4567-e89b-12d3-a456-426655440000"
+
nfts create-collection
Options
<file-path>
: Path to the JSON data file for the new collection.Example
apillon nfts create-collection ./nft-data.json
+
nfts mint-nft
Mints NFTs for a collection with a specific UUID.
Options
--uuid <collection-uuid>
: UUID of the collection to mint NFTs to.-a, --address <string>
: Address which will receive minted NFTs.-q --quantity <integer>
: Number of NFTs to mint. (default 1).Example
apillon nfts mint-nft --uuid "123e4567-e89b-12d3-a456-426655440000" --address "0xdAC17F958D2ee523a2206206994597C13D831ec7" --quantity 2
+
nfts nest-mint-nft
Nest mints NFT child collection to a parent collection with a specific UUID and parent NFT with id.
Options
-c, --parent-collection-uuid <collection-uuid>
: Parent collection UUID to which child NFTs will be minted to.-p, --parent-nft-id <string>
: Parent collection NFT id to which child NFTs will be minted to.-q, --quantity <integer>
: Number of child NFTs to mint (default 1).Example
apillon nfts nest-mint-nft --parent-collection-uuid "123e4567-e89b-12d3-a456-426655440000" --parent-nft-id 5 --quantity 2
+
nfts burn-nft
Burns an NFT for a collection with a specific UUID.
Options
--uuid <collection-uuid>
: Collection UUID.-t, --token-id <integer>
: NFT id which will be burned.Example
apillon nfts burn-nft --uuid "123e4567-e89b-12d3-a456-426655440000" --token-id 123
+
nfts transfer-collection
Transfers NFT collection ownership to a new wallet address.
Options
--uuid <collection-uuid>
: Collection UUID.-a, --address <string>
: Address which you want to transfer collection ownership to.Example
apillon nfts transfer-collection --uuid "123e4567-e89b-12d3-a456-426655440000" --address "0xdAC17F958D2ee523a2206206994597C13D831ec7"
+
nfts list-transactions
Lists NFT transactions for a specific collection UUID.
Options
--uuid <collection-uuid>
: Collection UUID.--status <integer>
: Transaction status (TransactionStatus enum, optional).Available choices:
PENDING = 1
+CONFIRMED = 2
+FAILED = 3
+ERROR = 4
+
--type <integer>
: Transaction type (TransactionType enum, optional).Available choices:
DEPLOY_CONTRACT = 1
+TRANSFER_CONTRACT_OWNERSHIP = 2
+MINT_NFT = 3
+SET_COLLECTION_BASE_URI = 4
+BURN_NFT = 5
+NEST_MINT_NFT = 6
+
Example
apillon nfts list-transactions --uuid "123e4567-e89b-12d3-a456-426655440000"
+
Example response
{
+ "items": [
+ {
+ "createTime": "2023-11-20T10:21:22.000Z",
+ "updateTime": "2023-11-20T10:23:31.000Z",
+ "chainId": "MOONBEAM",
+ "transactionType": "DEPLOY_CONTRACT",
+ "transactionStatus": "CONFIRMED",
+ "transactionHash": "0xab99e630f9475df92768b1e5d73f4..."
+ },
+ {
+ "createTime": "2023-11-20T11:55:13.000Z",
+ "updateTime": "2023-11-20T11:57:31.000Z",
+ "chainId": "MOONBEAM",
+ "transactionType": "MINT_NFT",
+ "transactionStatus": "CONFIRMED",
+ "transactionHash": "0x1ecfeeaeddfa0a39fc2ae1ec755d27..."
+ },
+ ...
+ ],
+ "total": 4
+}
+
CLI is particularly useful for CI/CD builds and pipelines.
Here's an example of how you can use the CLI tool in a CI/CD tool like GitHub Actions:
name: Deploy Website
+
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: 16
+
+ - name: Create dist folder
+ run: mkdir -p dist
+
+ - name: Copy files
+ run: |
+ cp *.html dist/
+ cp -r images dist/
+ cp -r style dist/
+ cp -r js dist/
+
+ ####
+ ## if you are using a framework for building web app, you can replace previous two step with the
+ ## appropriate command for generating static webpage, like an example bellow.
+ ## Find the correct command in your framework documentation. You may need to to change the
+ ## name of the source folder in the last step (CLI call)
+ ####
+
+ # - name: Build app
+ # run: npm run build
+
+ - name: Deploy website
+ env:
+ APILLON_API_KEY: ${{ secrets.APILLON_API_KEY }}
+ APILLON_API_SECRET: ${{ secrets.APILLON_API_SECRET }}
+ WEBSITE_UUID: ${{ secrets.WEBSITE_UUID }}
+ run: npx --yes @apillon/cli hosting deploy-website ./dist --uuid $WEBSITE_UUID --key $APILLON_API_KEY --secret $APILLON_API_SECRET
+ # Or alternatively you can use the run configuration below
+ # run: |
+ # npm i -g @apillon/cli
+ # apillon hosting deploy-website ./dist --uuid $WEBSITE_UUID --key $APILLON_API_KEY --secret $APILLON_API_SECRET
+
In this example, the GitHub Actions workflow is triggered when a push event occurs on the master branch. The workflow performs the following steps:
Make sure to setup secret variables with the values from Apillon platform.
That's it! You can now use this example as a starting point to deploy your website using the CLI tool in a CI/CD pipeline with GitHub Actions.
`,50),T={href:"https://github.com/Apillon/website-deploy-example/blob/main/.github/workflows/deploy.yml",target:"_blank",rel:"noopener noreferrer"};function _(w,A){const a=i("ExternalLinkIcon");return p(),l("div",null,[c,s("p",null,[s("a",u,[d,e(a)]),s("a",v,[b,e(a)])]),k,m,s("p",null,[n("To be able to use Apillon CLI, you must register an account at "),s("a",g,[n("Apillon.io"),e(a)]),n(", create a project and generate an API key with appropriate permissions. Also Node.js (version 16 or later) is required.")]),h,s("blockquote",null,[s("p",null,[n("For example responses and for an overview of all properties, refer to "),s("a",q,[n("the Apillon API wiki"),e(a)]),n(". Note: CLI responses may be dfferent from API responses.")])]),f,s("p",null,[n("Creates a new NFT collection. The JSON file needs to have the property structure as type "),s("a",y,[n("ICreateCollection"),e(a)]),n(" for EVM and "),s("a",I,[n("ICreateSubstrateCollection"),e(a)]),n(" for Substrate collections. An example object can be also seen on the "),s("a",x,[n("NFT SDK docs"),e(a)]),n(".")]),E,s("p",null,[n("You can also check a working example on "),s("a",T,[n("Github"),e(a)])])])}const D=o(r,[["render",_],["__file","6-apillon-cli.html.vue"]]);export{D as default}; diff --git a/assets/6-web3-social.html-b71e29ed.js b/assets/6-web3-social.html-b71e29ed.js new file mode 100644 index 00000000..59249e8b --- /dev/null +++ b/assets/6-web3-social.html-b71e29ed.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-592c909e","path":"/web3-services/6-web3-social.html","title":"Web3 Social","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Chat","slug":"chat","link":"#chat","children":[]}],"git":{"updatedTime":1715328683000,"contributors":[{"name":"Damjan Dimitrov","email":"damjandimitrov1@gmail.com","commits":1}]},"filePathRelative":"web3-services/6-web3-social.md"}');export{e as data}; diff --git a/assets/6-web3-social.html-c81f1620.js b/assets/6-web3-social.html-c81f1620.js new file mode 100644 index 00000000..19076203 --- /dev/null +++ b/assets/6-web3-social.html-c81f1620.js @@ -0,0 +1 @@ +import{_ as n,r,o as c,c as s,b as a,e,d as o,a as i}from"./app-270cc6ec.js";const l={},h=a("h1",{id:"web3-social",tabindex:"-1"},[a("a",{class:"header-anchor",href:"#web3-social","aria-hidden":"true"},"#"),e(" Web3 Social")],-1),d={href:"https://subsocial.network/",target:"_blank",rel:"noopener noreferrer"},p={href:"https://grillapp.net/c/hot-chats",target:"_blank",rel:"noopener noreferrer"},u=i('You can create the chat rooms via Apillon dashboard and integrate grill chat widget into you page via html
and javascript
. You can also manage you chat rooms via API and SDK for simple on the fly creation.
You can create a single chat room or a hub containing multiple chat rooms.
Hub A hub is a container of multiple chat rooms. When integrating the grill chat widget you can either show a single chat or a whole hub containing multiple chat rooms that the end user selects to chat in.
Channel Channel is the actual chat room the users can talk in. s
',5);function g(_,b){const t=r("ExternalLinkIcon");return c(),s("div",null,[h,a("p",null,[e("Apillon offers web3 alternatives for social networking between end users. This is powered by "),a("a",d,[e("subsocial network"),o(t)]),e(".")]),a("p",null,[e("Currently Apillon offers web3 chat alternative called "),a("a",p,[e("grill chat"),o(t)]),e(" that allows anonymous chatting which content can be displayed anywhere integrating the subsocial grill chat.")]),u])}const f=n(l,[["render",g],["__file","6-web3-social.html.vue"]]);export{f as default}; diff --git a/assets/6-web3-up-close.html-77cdd341.js b/assets/6-web3-up-close.html-77cdd341.js new file mode 100644 index 00000000..0e64c0fa --- /dev/null +++ b/assets/6-web3-up-close.html-77cdd341.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-da41cf52","path":"/about/6-web3-up-close.html","title":"Web3 and Apillon explained","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Polkadot, a complete service stack for Web3 builders","slug":"polkadot-a-complete-service-stack-for-web3-builders","link":"#polkadot-a-complete-service-stack-for-web3-builders","children":[{"level":3,"title":"Evolution of blockchain applications","slug":"evolution-of-blockchain-applications","link":"#evolution-of-blockchain-applications","children":[]},{"level":3,"title":"Polkadot offers flexibility and more","slug":"polkadot-offers-flexibility-and-more","link":"#polkadot-offers-flexibility-and-more","children":[]},{"level":3,"title":"Room for improvement","slug":"room-for-improvement","link":"#room-for-improvement","children":[]}]},{"level":2,"title":"Glossary","slug":"glossary","link":"#glossary","children":[{"level":3,"title":"The world of Web3","slug":"the-world-of-web3","link":"#the-world-of-web3","children":[]},{"level":3,"title":"Polkadot Network","slug":"polkadot-network","link":"#polkadot-network","children":[]},{"level":3,"title":"The Apillon platform","slug":"the-apillon-platform","link":"#the-apillon-platform","children":[]},{"level":3,"title":"Read more","slug":"read-more","link":"#read-more","children":[]}]}],"git":{"updatedTime":1715328683000,"contributors":[{"name":"Damjan Dimitrov","email":"damjandimitrov1@gmail.com","commits":1}]},"filePathRelative":"about/6-web3-up-close.md"}');export{e as data}; diff --git a/assets/6-web3-up-close.html-a27998e5.js b/assets/6-web3-up-close.html-a27998e5.js new file mode 100644 index 00000000..f73baf0d --- /dev/null +++ b/assets/6-web3-up-close.html-a27998e5.js @@ -0,0 +1 @@ +import{_ as l,r as s,o as d,c as h,b as e,e as a,d as t,w as i,a as r}from"./app-270cc6ec.js";const c={},p=r('Polkadot is a “blockchain of blockchains,” allowing many different types of chains to work and interact securely together within the same ecosystem. The network has already contributed great promise and significant progress by encouraging individual sovereignty over personal and business data. Andmore importantly, the integration of diverse parachains into a scalable and flexible network set a new standard for Web3 builders.
Bitcoin - the very first application of blockchain. Soon, the potential to create other decentralized applications grew big. Through the need for improved tools and technological basis, the next-gen blockchain upgrade sprouted in the form of the Ethereum network. A smart-contract-based technology made it possible to exchange value besides just the currency and gave way for developers to build and deploy dapps on top of Ethereum.
Ethereum - using the one-network-serves-all model resulted in limitations like flexibility, scalability, and sovereignty. While building dapps on Ethereum proved beneficial for many industry verticals and demonstrated innovations increase, the mass-scale adoption still fell behind because of the rigid network system and limited scalability of transactions.
The Polkadot network is becoming the forefront of a scalable Web3 universe-building and mass adoption of blockchain – by connecting different blockchains into one unified network. Unlike layer-1 blockchains, layer 0 of Polkadot allows for multiple layer-1 blockchains to be built on top of it. The Polkadot team even created a framework called Substrate, where developers can build blockchains that work independently from one another.
Polkadot’s layer 0, or the so-called relay chain, executes minimal basic actions in terms of network security and makes the communication between chains possible. The blockchains as parachains design their own governance structures, fine-tune their functionalities, and (usually) serve a specific use case purpose.
It’s a win-win situation:
Parachains have more flexibility in building the architecture and therefore contribute better, more specialized services with improved efficiency. Polkadot as an ecosystem offers higher scalability by processing transactions on different chains in parallel and gives way for more innovation due to more specialized parachains.
Polkadot is currently the leading, highest value, and most secure proof-of-stake platform. Nevertheless, every technology’s success can benefit from a reliable support system that can enhance its adoption and bring it to the masses. Each innovation is only as strong as its community of users.
Web3 developers still face considerable challenges when building on Web3. Polkadot offers an excellent foundation, but there’s still enormous amounts of time that need to be invested in the research and development by the builders.
By making Web3 easier to build on, the Apillon platform will assist in the adoption of use-case-specific Polkadot parachains and bring the utility of Web3 to enterprises and end-users that would benefit from (at least partially) blockchain-based processes.
Web3 is a next-generation, decentralized, and democratic version of the web where the way users control and exchange their data is redefined to empower them to partake in the building of the web, take ownership of their data and identity, as well as co-govern the processes. Web3 is built upon an infrastructure that incorporates blockchain technology and decentralized means of data and asset exchange.
Smart contracts are digital automatic agreements that are stored on a blockchain. Secure and trustless programs that execute functions when pre-determined terms and conditions are met, smart contracts deliver transparency, accuracy, and efficiency. At a basic level, smart contracts follow "if/when… then" statements that are written into code.
In a decentralized network, there is no single central authority that controls all data and governs the processes of data and asset exchange. Instead, a decentralized network is controlled by the pre-determined logic that’s written into code on the blockchain. As the code is stored on a decentralized network of nodes around the globe, this delivers an infrastructure that enables peer-to-peer data and asset exchange, which can allow users to regain control over their data and identity.
Nodes create, receive and communicate information that they then store or relay to other nodes. The definition of a node is directly tied to the type of network it participates in – in a blockchain network, blockchain nodes are devices, usually computers, that run the blockchain’s software to validate and broadcast transactions as well as secure the network. Blockchain nodes communicate with each other, and the more nodes there are, the more decentralized the blockchain network is.
A decentralized application (dapp) is built upon a decentralized system for executing its operations. Its backend code runs on a decentralized network, usually a blockchain, instead of a centralized server. This means the operations are inherently immutable, irreversible, and sealed on the decentralized network. The app’s data and files, for example, can be hosted and stored on a decentralized network of nodes – once deployed, the files can’t be taken down or tampered with.
The word ‘trustless’ describes the online exchange of data and assets in Web3 that puts trust into the logic of the code in the backend instead of a single central authority. The core infrastructure of Web3 is controlled by the code in the backend, which eliminates the need to trust third-party service providers or big tech to manage the exchange of data and assets between the users.
Contrary to files, websites, or apps that are stored and hosted on central servers with a single point of access and failure, decentralized or Web3-based assets, websites, or apps can be stored and hosted on a global decentralized network of nodes. This defies single censorship, tampering, or single-point attacks, making them unstoppable.
Polkadot is a sharded blockchain. Connecting several blockchains into a unified network, Polkadot Network allows the separate chains to securely process transactions and exchange data at the same time. Each chain can be optimized for a specific use case while still being able to communicate with the other chains in the network. This unique model is called Polkadot’s heterogeneous sharding model.
The relay chain is Polkadot’s main chain that executes minimal basic actions to ensure network security. It also enables communications between (para)chains.
Parachains are different Polkadot blockchains that connect to the main relay chain. With their own governance structures and different functionalities, parachains serve a specific use case, yet they are able to communicate with each other. The ability of Polkadot’s ecosystem to process transactions on different chains in parallel delivers high scalability.
Web3 project is a website, program, or app that’s built using decentralized technologies. Even standard Web2 projects can be upgraded to the Web3-level by making small changes and incorporating blockchain technology. The Apillon platform makes building Web3 projects simpler and faster.
A freemium business model offers basic services free of charge, while a premium is charged for the more advanced services and features. Currently, all features available on the Apillon platform are free of charge. In the later stages, the basic Web3 services will remain free, while the more advanced features will be available through unified stable pricing.
Certain tokens can be staked by locking them up for a period of time for various reasons, e.g. to support the operation of a Web3 project or network, in exchange for a reward, typically an interest on the staked amount.
To initiate the OAuth flow for the user, use the following code to open Apillon's OAuth website as a pop-up and prompt your users to complete the OAuth flow. The session token passed as a query parameter is obtained from the Apillon API (see Server section below).
Additionally, an event listener is added for the main app/website to handle the successful completion of the OAuth flow by the user, which grants an authentication token to the user. The token is then used to verify the login through Apillon's API, which finally returns the user's email address on Apillon.
async function openOAuthPopup() {
+ const sessionToken = await getAuthToken();
+ oAuthWindow = window.open(
+ \`https://oauth.apillon.io/?embedded=1&token=\${sessionToken}\`,
+ 'Apillon OAuth Form',
+ \`height=\${900} width=\${450} resizable=no\`
+ );
+}
+
+window.addEventListener('message', async event => {
+ if (!event.origin?.includes('apillon.io')) return;
+
+ if (!event.data.verified) {
+ throw new Error('Invalid OAuth verification');
+ }
+ // Close OAuth popup window
+ oAuthWindow?.close();
+
+ verifyUserLogin(event.data.authToken);
+}, false);
+
The server-side part of the OAuth implementation contains the logic to query the Apillon API, obtain a session token to initiate the OAuth flow, and verify the user log-in when the flow is finished.
Obtain a session token from the Apillon API to interact with Apillon's OAuth protocol.
`,8),T={class:"split_content"},P={class:"split_side"},j=n("br",null,null,-1),x=n("div",{class:"language-bash line-numbers-mode","data-ext":"sh"},[n("pre",{class:"language-bash"},[n("code",null,[n("span",{class:"token function"},"curl"),s(),n("span",{class:"token parameter variable"},"--location"),s(),n("span",{class:"token parameter variable"},"--request"),s(" GET "),n("span",{class:"token string"},'"https://api.apillon.io/auth/session-token"'),s(),n("span",{class:"token punctuation"},"\\"),s(` +`),n("span",{class:"token parameter variable"},"--header"),s(),n("span",{class:"token string"},'"Authorization: Basic :credentials"'),s(),n("span",{class:"token punctuation"},"\\"),s(` +`),n("span",{class:"token parameter variable"},"--header"),s(),n("span",{class:"token string"},'"Content-Type: application/json"'),s(),n("span",{class:"token punctuation"},"\\"),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),C={class:"split_side"},E=n("br",null,null,-1),G=n("div",{class:"language-json line-numbers-mode","data-ext":"json"},[n("pre",{class:"language-json"},[n("code",null,[n("span",{class:"token punctuation"},"{"),s(` + `),n("span",{class:"token property"},'"id"'),n("span",{class:"token operator"},":"),s(),n("span",{class:"token string"},'"0da29b5a-8a8b-473b-9f97-3183819263f4"'),n("span",{class:"token punctuation"},","),s(` + `),n("span",{class:"token property"},'"status"'),n("span",{class:"token operator"},":"),s(),n("span",{class:"token number"},"200"),n("span",{class:"token punctuation"},","),s(` + `),n("span",{class:"token property"},'"data"'),n("span",{class:"token operator"},":"),s(),n("span",{class:"token punctuation"},"{"),s(` + `),n("span",{class:"token property"},'"sessionToken"'),n("span",{class:"token operator"},":"),s(),n("span",{class:"token string"},'"eyJhbGciOiJIUzI1..."'),s(` + `),n("span",{class:"token punctuation"},"}"),s(` +`),n("span",{class:"token punctuation"},"}"),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),S=n("h3",{id:"verify-user-login",tabindex:"-1"},[n("a",{class:"header-anchor",href:"#verify-user-login","aria-hidden":"true"},"#"),s(" Verify user login")],-1),U=n("p",null,`After the user has completed the OAuth flow, verify they have successfully logged in with the generated OAuth token from the "message" event handler. As a response, receive the user's Apillon email address.`,-1),L={class:"split_content"},q={class:"split_side"},z=n("br",null,null,-1),B=n("div",{class:"language-bash line-numbers-mode","data-ext":"sh"},[n("pre",{class:"language-bash"},[n("code",null,[n("span",{class:"token function"},"curl"),s(),n("span",{class:"token parameter variable"},"--location"),s(),n("span",{class:"token parameter variable"},"--request"),s(" POST "),n("span",{class:"token string"},'"https://api.apillon.io/auth/verify-login"'),s(),n("span",{class:"token punctuation"},"\\"),s(` +`),n("span",{class:"token parameter variable"},"--header"),s(),n("span",{class:"token string"},'"Authorization: Basic :credentials"'),s(),n("span",{class:"token punctuation"},"\\"),s(` +`),n("span",{class:"token parameter variable"},"--header"),s(),n("span",{class:"token string"},'"Content-Type: application/json"'),s(),n("span",{class:"token punctuation"},"\\"),s(` +--data-raw `),n("span",{class:"token string"},[s(`"{ + `),n("span",{class:"token entity",title:'\\"'},'\\"'),s("token"),n("span",{class:"token entity",title:'\\"'},'\\"'),s(": "),n("span",{class:"token entity",title:'\\"'},'\\"'),s("eyJhbGciOiJIUzI1..."),n("span",{class:"token entity",title:'\\"'},'\\"'),s(` +}"`)]),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1),J={class:"split_side"},N=n("br",null,null,-1),V=n("div",{class:"language-json line-numbers-mode","data-ext":"json"},[n("pre",{class:"language-json"},[n("code",null,[n("span",{class:"token punctuation"},"{"),s(` + `),n("span",{class:"token property"},'"id"'),n("span",{class:"token operator"},":"),s(),n("span",{class:"token string"},'"de2cf1e7-0dfe-4378-ab77-98cbc9a00496"'),n("span",{class:"token punctuation"},","),s(` + `),n("span",{class:"token property"},'"status"'),n("span",{class:"token operator"},":"),s(),n("span",{class:"token number"},"200"),n("span",{class:"token punctuation"},","),s(` + `),n("span",{class:"token property"},'"data"'),n("span",{class:"token operator"},":"),s(),n("span",{class:"token punctuation"},"{"),s(` + `),n("span",{class:"token property"},'"email"'),n("span",{class:"token operator"},":"),s(),n("span",{class:"token string"},'"apillon-user@mail.com"'),s(` + `),n("span",{class:"token punctuation"},"}"),s(` +`),n("span",{class:"token punctuation"},"}"),s(` +`)])]),n("div",{class:"line-numbers","aria-hidden":"true"},[n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"}),n("div",{class:"line-number"})])],-1);function K(R,Y){const t=l("ExternalLinkIcon"),p=l("CodeDiv"),o=l("CodeGroupItem"),i=l("CodeGroup");return r(),u("div",null,[k,n("p",null,[s("You can easily integrate Apillon's OAuth protocol into your own project or website. If you don't have an Apillon account or project already, get started on the "),n("a",v,[s("Apillon dashboard"),a(t)]),s(".")]),n("p",null,[s("Once you have created a project, navigate to the project's "),n("a",m,[s("API keys"),a(t)]),s(" section in your project settings. Generate an API key for the Authentication service with the "),b,s(" permission. This API key will be used to interact with Apillon's API, generate an OAuth session, and verify a user log-in. Make sure you store your API key and your API key secret in a secure manner.")]),_,n("ol",null,[n("li",null,[n("p",null,[g,s(" If you don't have an Apillon account or project yet, create one on the "),n("a",f,[s("Apillon dashboard"),a(t)]),s(".")])]),n("li",null,[n("p",null,[y,s(" Go to your project's settings and navigate to the "),n("a",A,[s("API keys section"),a(t)]),s(". Generate an API key for the Authentication service with the KEY_EXECUTE permission.")])]),w]),n("blockquote",null,[n("p",null,[s("For a complete NodeJS demo of the whole OAuth flow, refer to "),n("a",I,[s("this Github repo"),a(t)])])]),O,a(p,null,{default:e(()=>[s("GET /auth/session-token")]),_:1}),n("div",T,[n("div",P,[j,a(i,null,{default:e(()=>[a(o,{title:"cURL",active:""},{default:e(()=>[x]),_:1})]),_:1})]),n("div",C,[E,a(i,null,{default:e(()=>[a(o,{title:"Response"},{default:e(()=>[G]),_:1})]),_:1})])]),S,U,a(p,null,{default:e(()=>[s("POST /auth/verify-login")]),_:1}),n("div",L,[n("div",q,[z,a(i,null,{default:e(()=>[a(o,{title:"cURL",active:""},{default:e(()=>[B]),_:1})]),_:1})]),n("div",J,[N,a(i,null,{default:e(()=>[a(o,{title:"Response"},{default:e(()=>[V]),_:1})]),_:1})])])])}const D=c(h,[["render",K],["__file","7-apillon-oauth-integration.html.vue"]]);export{D as default}; diff --git a/assets/7-community.html-26fa6d30.js b/assets/7-community.html-26fa6d30.js new file mode 100644 index 00000000..3b1fc06b --- /dev/null +++ b/assets/7-community.html-26fa6d30.js @@ -0,0 +1 @@ +const o=JSON.parse('{"key":"v-929e013e","path":"/about/7-community.html","title":"Community","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Join Apillon Community","slug":"join-apillon-community","link":"#join-apillon-community","children":[]},{"level":2,"title":"Polkadot Treasury Proposal","slug":"polkadot-treasury-proposal","link":"#polkadot-treasury-proposal","children":[{"level":3,"title":"Common good and open source","slug":"common-good-and-open-source","link":"#common-good-and-open-source","children":[]},{"level":3,"title":"Proposal: Apillon, Polkadot, Web3","slug":"proposal-apillon-polkadot-web3","link":"#proposal-apillon-polkadot-web3","children":[]}]}],"git":{"updatedTime":1715328683000,"contributors":[{"name":"Damjan Dimitrov","email":"damjandimitrov1@gmail.com","commits":1}]},"filePathRelative":"about/7-community.md"}');export{o as data}; diff --git a/assets/7-community.html-375ba728.js b/assets/7-community.html-375ba728.js new file mode 100644 index 00000000..b4876a7b --- /dev/null +++ b/assets/7-community.html-375ba728.js @@ -0,0 +1,98 @@ +import{_ as i,r,o as s,c as l,b as e,e as o,d as t,f as d,a}from"./app-270cc6ec.js";const p={},h=a('Become part of the Apillon community and stay up to date with the latest news.
',4),c={href:"https://twitter.com/apillon",target:"_blank",rel:"noopener noreferrer"},u={href:"https://discord.gg/yX3gTw36C4",target:"_blank",rel:"noopener noreferrer"},m={href:"https://t.me/Apillon",target:"_blank",rel:"noopener noreferrer"},f={href:"https://www.reddit.com/r/apillon/",target:"_blank",rel:"noopener noreferrer"},g={href:"https://blog.apillon.io",target:"_blank",rel:"noopener noreferrer"},b={href:"https://www.linkedin.com/company/apillon",target:"_blank",rel:"noopener noreferrer"},w={href:"https://www.youtube.com/channel/UCH5DxaOQ1cdCgNb_nk-5_ug/about",target:"_blank",rel:"noopener noreferrer"},y={href:"https://github.com/Apillon",target:"_blank",rel:"noopener noreferrer"},v=e("h2",{id:"polkadot-treasury-proposal",tabindex:"-1"},[e("a",{class:"header-anchor",href:"#polkadot-treasury-proposal","aria-hidden":"true"},"#"),o(" Polkadot Treasury Proposal")],-1),A={href:"https://blog.apillon.io/apillon-aims-to-foster-web3-adoption-as-a-common-good-infrastructure-solution-on-polkadot-6566508190c5",target:"_blank",rel:"noopener noreferrer"},k=a('Backed by Polkadot Treasury funding and community support, the Apillon platform would transition to an open-source product with permanent freemium access and community governance. By becoming a common good project, Apillon would help spur Web3 adoption, Polkadot ecosystem growth, and DOT utility.
Here are the most frequent questions and answers to help you navigate and understand the weight of the proposal.
Polkadot’s common good and open-source approach aims to innovate services that benefit projects and users across the whole ecosystem.
With many new protocol-layer projects regularly developed on Polkadot, the ecosystem is in dire need of an application-layer solution that would allow developers to easily onboard the Polkadot ecosystem and bring Web3 to a mainstream level.
With Apillon transforming into a common good solution, the Polkadot ecosystem would get a forever-freemium Web3 development platform. Every new user on the Apillon platform would mean a new developer for Polkadot, boosting the adoption of Web3 technologies. Through DOT-based governance, the Polkadot’s native token would get another utility boost and purpose in driving the network’s adoption.
With decentralized governance, existing and new platform users would get to decide on parachain integrations and the evolution of the Apillon platform, thus collaboratively innovating the future of Web3.
Apillon would make a strong commitment to the community and the ecosystem to achieve the set milestones, spend resources as described in the Proposal, and pursue the vision of the developer community.
Apillon’s Proposal for Polkadot’s Treasury funding seeks community support to transform into an open-source infrastructure on Polkadot.
The funding would have the following direct consequences on Apillon:
With operational costs covered by the Treasury for two years, Apillon would be able to incentivize developers with freemium plans at no additional fees.
After the end of the two-year period, Apillon would introduce a price margin on top of parachain service cost. However, this would be non-profitable and controlled via governance, allowing the Apillon team to survive on fees to keep the project alive and in the best possible shape.
Apillon’s Treasury-backed transition to open-source presents a strong obligation towards the community. Therefore, The Freemium plan will remain permanently or until the project’s governance decides otherwise.
In the current stage, three main pillars of Apillon would require implementation of decentralized control:
With DOT-based governance, the developer community would be able to co-decide the development direction of the Apillon platform, making it more agile in addressing market demands.
Apillon’s product governance would address questions such as:
The Apillon platform would further improve the Polkadot ecosystem and the Web3 space with:
Easier, faster, and cheaper Web3 development would bring developers and other stakeholders:
Apillon's contribution to Web3 adoption and the growth of the Polkadot ecosystem is significant and lies in its provision of an open-source common good development platform. A user-friendly and accessible environment for building Web3 applications would attract more developers, including those transitioning from Web2 to Web3. The influx of talent and ideas would fuel the creation of diverse and innovative decentralized applications built on the Polkadot network, leading to increased usage, adoption, and overall ecosystem growth. Additionally, by providing a streamlined development experience and facilitating the integration of various parachains, Apillon's platform contributes to the scalability and interoperability of the network, making it more appealing to developers and users alike.
The backing of Polkadot Treasury funding would tremendously impact the Apillon platform. It would provide financial support and resources to develop further and enhance the platform's capabilities, enabling Apillon to accelerate its efforts in building a robust Web3 infrastructure. The funding support would help Apillon attract top talent, foster innovation, and execute its roadmap effectively, ultimately contributing to the growth and adoption of Web3 technologies within the Polkadot ecosystem.
The opportunity for Apillon to become an open-source common good infrastructure platform on Polkadot opens up opportunities for early contributors, too.
Polkadot Treasury funding of the Apillon two-and-a-half-year development ensures uninterrupted work on both preset and jointly agreed upon milestones to deliver the most requested and needed Web3 services and features to the developer community.
With every developer using the Apillon platform, the Polkadot and Web3 ecosystems gain a new contributor. Faster growth of Apillon means accelerated expansion and collaboration of both established and up-and-coming Web3 projects.
In addition to the financial aspect, Polkadot Treasury would provide a much-appreciated approval from the wider Web3 community, acknowledging Apillon as the go-to Web3 development platform - that would significantly increase the future value of the project.
Apillon has always greatly appreciated the community support and trust in the project.
At this early point of the referendum process, the benefits for early investors are still undefined. The Apillon team kindly asks for everyone’s patience while the optimal options for the community are being explored.
One of Proposal’s main points is Apillon transitioning to DOT-based governance and operations. Hence, the launch of the NCTR token is postponed until after the end of the period set in the Proposal.
Field | Type | Description | Required |
---|---|---|---|
contractType | number | Type of the computing contract. Available options: 1 = SCHRODINGER | Yes |
name | string | Name of the computing contract | Yes |
description | string | Description of the computing contract | No |
bucket_uuid | string | UUID of the bucket for storing encrypted files from the computing contract. If this is not provided, a new bucket will be created | No |
Data specific for Schrodinger's NFT contract type
Field | Type | Description | Required |
---|---|---|---|
contractData.nftContractAddress | string | Contract address of the NFT collection whose tokens will be used for file decryption | No |
contractData.nftChainRpcUrl | string | RPC URL of the blockchain the NFT contract resides on | Yes |
contractData.restrictToOwner | boolean | If true, only the owner can encrypt files via the contract (Default: true) | Yes |
Code | Description |
---|---|
40412003 | Bucket from given bucket_uuid param not found |
42200202 | Contract type not present |
42200203 | Contract type not valid |
42200204 | Contract name not present |
42200205 | Contract name not valid (length in range 1-255) |
42200206 | Contract description not valid (length in range 1-1000) |
42200210 | Computing field not present |
42200211 | Computing contract data not valid |
50012003 | Error deploying contract |
Field | Type | Description | Required |
---|---|---|---|
contractStatus | number | 0 (created), 1 (deploy initiated), 2 (deploying), 3 (deployed), 4 (transferring), 5 (transferred), 6 (failed) | No |
Field | Type | Description | Required |
---|---|---|---|
uuid | string | The UUID of the computing contract | Yes |
Based on your request to generate a table for the response fields that matches the structure seen in the bucket API documentation, here is how you can format the response fields for the "Get Computing Contract" section:
Field | Type | Description |
---|---|---|
contractUuid | string | The unique identifier of the computing contract. |
bucketUuid | string | The UUID of the bucket associated with this computing contract, where encrypted files are stored. |
name | string | The name of the computing contract. |
description | string | A description of what the computing contract is used for. |
contractType | number | The type of computing contract. For example, 1 could represent SCHRODINGER. |
contractStatus | number | The current status of the computing contract. Possible values: 0 (created), 1 (deploy initiated), 2 (deploying), 3 (deployed), 4 (transferring), 5 (transferred), 6 (failed) |
contractAddress | string | The blockchain address of the computing contract. This can be null if not yet deployed. |
deployerAddress | string | The address of the entity that deployed the computing contract. |
transactionHash | string | The transaction hash of the contract deployment. This can be null if not yet deployed. |
data | object | An object containing additional data related to the computing contract. |
data.nftContractAddress | string | The contract address of the NFT collection used for file decryption. |
data.nftChainRpcUrl | string | The RPC URL of the blockchain where the NFT contract resides. |
data.restrictToOwner | boolean | Indicates if only the owner can encrypt and decrypt files via the contract. |
data.ipfsGatewayUrl | string | The URL of the IPFS gateway used by the computing contract. |
data.clusterId | string | An identifier for the cluster used by the computing contract. |
Field | Type | Description | Required |
---|---|---|---|
uuid | string | The UUID of the computing contract | Yes |
Field | Type | Description | Required |
---|---|---|---|
transactionType | number | The type of transaction. Possible values: 1 (deploy contract), 2 (transfer contract ownership), 3 (deposit to contract cluster), 4 (assign cid to nft) | |
transactionStatus | number | The current status of the transaction. Possible values: 1 (pending), 2 (confirmed), 3 (failed), 4 (error), 5 (worker success), 6 (worker failed) |
Field | Type | Description |
---|---|---|
status | number | The status code of the transaction. |
walletAddress | string | The wallet address which initated the transaction. |
transactionType | number | The type of transaction. Possible values: 1 (deploy contract), 2 (transfer contract ownership), 3 (deposit to contract cluster), 4 (assign cid to nft) |
transactionStatus | number | The current status of the transaction. Possible values: 1 (pending), 2 (confirmed), 3 (failed), 4 (error), 5 (worker success), 6 (worker failed) |
transactionStatusMessage | string | A message providing more details about the transaction status. |
transactionHash | string | The hash of the transaction on the blockchain. |
Field | Type | Description | Required |
---|---|---|---|
uuid | string | The UUID of the computing contract | Yes |
Field | Type | Description | Required |
---|---|---|---|
accountAddress | string | Wallet address of the new owner | Yes |
Code | Description |
---|---|
40012003 | Invalid address to transfer to |
40012004 | Transaction for transfer already exists |
50012004 | Transfer contract server error |
50012009 | Contract not in status 3 (Deployed) |
50012010 | Contract transferring or already transferred |
Success status
Field | Type | Description | Required |
---|---|---|---|
uuid | string | The UUID of the computing contract | Yes |
Field | Type | Description | Required |
---|---|---|---|
content | string | Content to be encrypted. If the content is an image, the format needs to be base64. | Yes |
Code | Description |
---|---|
50012009 | Contract not in status 3 (Deployed) |
50012011 | Encrypt content server error |
The encrypted content, which has been encrypted via the encryption key stored on the schrodinger contract
Field | Type | Description | Required |
---|---|---|---|
uuid | string | The UUID of the computing contract | Yes |
Field | Type | Description | Required |
---|---|---|---|
cid | string | CID of the file where the encrypted content is stored | Yes |
nftId | number | Token ID of the NFT which will be used for decryption | Yes |
Code | Description |
---|---|
50012009 | Contract not in status 3 (Deployed) |
50012012 | Assign CID to NFT server error |
Success status
The NCTR token (pronounced “nectar”) is Apillon’s native utility token and sustains Apillon functionalities when interacting with linked Polkadot parachains and transacting with their protocols.
It represents a uniform means of value exchange in the process of building Web3 projects on the Apillon platform, as it replaces the need to interact with individual tokens of utilized parachains.
For every value-based action on Apillon, the platform manages the utility of parachains’ features in their native tokens in the back end so that developers only deal with NCTR and do not have to worry about acquiring or exchanging other tokens.
The Apillon business model also supports payments in fiat (EUR and USD), further boosting adoption among first-time adopters of Web3. The platform converts it to NCTR to unlock Apillon functionalities.
The tokenomics of the NCTR token and Apillon’s business model rely on decision-making by two main actors:
The NCTR tokenomics are, therefore, built to incentivize both actors and ensure all NCTR holders have a relevant say in the platform’s future and get rewarded for participating in Apillon’s governance.
Developers are the force that drives Web3 and the first group of participants in the Apillon tokenomics. The platform is designed to cater to their ever-evolving needs, respond to their expectations, challenges, and workflows, but also to empower them to voice feedback and impact the course of platform development through governance.
In the vast and ever-evolving field of blockchain technology, it is increasingly challenging for all stakeholders to track all the newly launched projects and evaluate their offerings. To double down on the Web3 research power, Apillon empowers its main users, the developers, to list new service candidates via governance and vote on preferred services to be implemented in the upcoming platform upgrades. Through user-powered governance and a token-curated registry of services, the platform continues to evolve and optimize its offering while preventing any potential censorship.
No Web3 project can stand the test of time and market without community backing. The Apillon community gathers NCTR token holders and Web3 developers. The NCTR token is designed to serve and reward everyone who contributes to the growth of the Apillon platform and boosts Web3 adoption, aligned with Apillon’s primary mission. All NCTR token holders can be Web3 builders, either by developing functional Web3 products or growing the ecosystem, and thus have the right to contribute to the NCTR token pledging and Apillon’s inclusive governance.
The NCTR token is designed to be a multi-purpose and widely used asset, covering a range of utility cases for platform users and community members. NCTR token utilities are spanning across three main pillars:
Each pillar further serves both NCTR Developers and NCTR Holders, offering distinct opportunities for utilizing the NCTR token.
Apillon's product governance does not imply an establishment of Apillon as an end-to-end DAO where participants propose and vote on everything, business decisions included. Instead, the governance aspect is focused on the product, i.e., the Apillon platform, and its narrow scope of user-oriented services and features that participants can help control and upgrade.
The second NCTR token’s utility is aimed at incentivizing both tokenomics actors in various ways while promoting Web3 adoption. Adoption is, therefore, the main measured KPI, and when achieved, it awards NCTR tokens to rightful recipients.
Incentivization through NCTR spans several use cases, including proof of adoption (PoA), prebuilt solutions grants, prebuilt solutions marketplace, SDK grands, protocol integration grants, bug bounties, authorship royalties, referral earnings, and many more.
Besides providing few-click-deploy Web3 services and straightforward APIs to integrated parachains, Apillon offers an underlying payments module that simplifies the use of Web3 technologies. The payments module removes the need for developers to acquire and maintain a positive balance of tokens to pay for underlying services. This way, Apillon can charge for its services in a traditional Saas model, where users pay monthly subscriptions with fiat using credit cards and unlock access to a fixed amount of Web3 services and features. The secondary path to pay for Apillon services is through the NCTR token. The Governance and Incentivization utilities of the NCTR token deliver a fantastic opportunity for synergy between NCTR payments and product governance.
The NCTR token is an ERC-20-compatible token minted on the Astar network.
Total supply: 150,000,000 NCTR
Learn more about the NCTR token, its role in the Apillon business model, and the general tokenomics.
',31),d={href:"https://k2k4r8lq6jzzcp7q2sjt5jhuuyjnb7y1nlfase5zepjzxwjttbxp8wp9.ipns.nectarnode.io/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaWQiOiJrMms0cjhscTZqenpjcDdxMnNqdDVqaHV1eWpuYjd5MW5sZmFzZTV6ZXBqenh3anR0YnhwOHdwOSIsInByb2plY3RfdXVpZCI6IjI4N2JkZmM3LTUwMzgtNDAzNi1iMzgzLWFjMzk5ZjBkZTdiMSIsImlhdCI6MTcwOTI5ODQ4OCwic3ViIjoiSVBGUy10b2tlbiJ9.GoxuuxoRGOIsEtgdumzPFhKWcGJufvW0KXeBeFlnjCo",target:"_blank",rel:"noopener noreferrer"},p={href:"https://discord.gg/yX3gTw36C4",target:"_blank",rel:"noopener noreferrer"};function u(m,f){const n=i("ExternalLinkIcon");return r(),s("div",null,[h,e("p",null,[e("a",d,[t("Access Apillon's NCTR Tokenomics Whitepaper here"),o(n)])]),e("p",null,[e("a",p,[t("Become a part of the Apillon Community"),o(n)])])])}const v=a(c,[["render",u],["__file","8-nctr-token.html.vue"]]);export{v as default}; diff --git a/assets/8-nctr-token.html-c41c7f8e.js b/assets/8-nctr-token.html-c41c7f8e.js new file mode 100644 index 00000000..35c53d9a --- /dev/null +++ b/assets/8-nctr-token.html-c41c7f8e.js @@ -0,0 +1 @@ +const e=JSON.parse('{"key":"v-564790ea","path":"/about/8-nctr-token.html","title":"NCTR token","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Tokenomics Actors","slug":"tokenomics-actors","link":"#tokenomics-actors","children":[{"level":3,"title":"NCTR Developers","slug":"nctr-developers","link":"#nctr-developers","children":[]},{"level":3,"title":"NCTR Holders","slug":"nctr-holders","link":"#nctr-holders","children":[]}]},{"level":2,"title":"Token Utilities","slug":"token-utilities","link":"#token-utilities","children":[{"level":3,"title":"Governance","slug":"governance","link":"#governance","children":[]},{"level":3,"title":"Incentivization","slug":"incentivization","link":"#incentivization","children":[]},{"level":3,"title":"Payments","slug":"payments","link":"#payments","children":[]}]},{"level":2,"title":"Tokenomics","slug":"tokenomics","link":"#tokenomics","children":[{"level":3,"title":"Technical Token Parameters","slug":"technical-token-parameters","link":"#technical-token-parameters","children":[]},{"level":3,"title":"Tokenomics Whitepaper","slug":"tokenomics-whitepaper","link":"#tokenomics-whitepaper","children":[]}]}],"git":{"updatedTime":1715328683000,"contributors":[{"name":"Damjan Dimitrov","email":"damjandimitrov1@gmail.com","commits":1}]},"filePathRelative":"about/8-nctr-token.md"}');export{e as data}; diff --git a/assets/9-social-api.html-010a3fe5.js b/assets/9-social-api.html-010a3fe5.js new file mode 100644 index 00000000..24c2de1e --- /dev/null +++ b/assets/9-social-api.html-010a3fe5.js @@ -0,0 +1,132 @@ +import{_ as p,r,o as u,c as h,b as s,e as n,d as e,w as a,a as i}from"./app-270cc6ec.js";const b={},k=s("h1",{id:"social-api",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#social-api","aria-hidden":"true"},"#"),n(" Social API")],-1),m={href:"https://github.com/dappforce/grillchat/tree/main/integration",target:"_blank",rel:"noopener noreferrer"},v=s("strong",null,"Hub",-1),_=s("strong",null,"Channel",-1),g=s("strong",null,"Note:",-1),f={href:"https://app.apillon.io/dashboard/service/social",target:"_blank",rel:"noopener noreferrer"},y=s("p",null,"Use the Social API to create hubs and channels from your application. For example: you can create a channel (chatroom) for each NFT collection in your marketplace.",-1),A=s("p",null,"In all cURL examples, parameters with a colon as a prefix should be replaced with real values.",-1),T=s("h2",{id:"channels",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#channels","aria-hidden":"true"},"#"),n(" Channels")],-1),x=s("p",null,"By default, a channel is created inside a hub, therefore a hub can contain multiple channels. The grill widget displays hubs as a list of channels inside it, and a channel is displayed as a chatroom. A channel can be created inside Apillon's default hub or you can create your own hub and create channels within it.",-1),I=s("h3",{id:"list-channels",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#list-channels","aria-hidden":"true"},"#"),n(" List channels")],-1),w={class:"split_content"},P={class:"split_side"},q=s("h4",{id:"query-parameters",tabindex:"-1"},[s("a",{class:"header-anchor",href:"#query-parameters","aria-hidden":"true"},"#"),n(" Query parameters")],-1),R=i('Name | Description | Required |
---|---|---|
hubUuid | Parent hub unique identifier | false |
Each item is an instance of channel class, with below properties:
Field | Type | Description |
---|---|---|
channelUuid | string | Channel unique identifier |
hubUuid | string | Unique identifier of the channel's parent Hub |
channelId | number | Channel id on Subsocial chain. This id is used in widget. |
status | number | Channel status (1: draft - deploying to chain, 5: active, 100: error ) |
title | string | Channel name |
body | string | Channel body - short description |
tags | string | Comma separated tags |
createTime | DateTime | Item create time |
updateTime | DateTime | Item last update time |
Name | Description | Required |
---|---|---|
channelUuid | Channel unique identifier | true |
Code | Description |
---|---|
40419001 | Channel does not exists |
Response is an instance of channel class, described above.
Name | Type | Description | Required |
---|---|---|---|
title | string | Channel name | true |
body | string | Channel content/description | true |
tags | string | Comma separated tags | false |
hubUuid | string | Hub unique identifier - if not specified the channel is created inside a default hub | false |
Code | Description |
---|---|
40419001 | Specified hub does not exist |
42219002 | Body is missing required properties |
50019004 | Parent hub is in invalid state. It is probably not yet transmitted and confirmed on the chain (status = 1) |
50019003 | Internal error - Apillon was unable to create channel. |
Response is an instance of channel class, described above.
Each item is an instance of hub class, with below properties:
Field | Type | Description |
---|---|---|
hubUuid | string | Hub unique identifier |
hubId | number | Hub id on Subsocial chain. This id is used in widget or in grillapp.net. Example: https://grillapp.net/12927 . |
status | number | Hub status (1: draft - deploying to chain, 5: active, 100: error ) |
name | string | Hub name |
about | string | Hub about - short description |
tags | string | Comma separated tags |
numOfChannels | number | Number of channels in hub |
createTime | DateTime | Item create time |
updateTime | DateTime | Item last update time |
Name | Description | Required |
---|---|---|
hubUuid | Hub UUID, visible in developer console | true |
Code | Description |
---|---|
40419001 | Hub does not exists |
Response is an instance of hub class, described above. Only difference is field numOfChannels
, which is not returned by this endpoint.
Name | Type | Description | Required |
---|---|---|---|
name | string | Hub name. | true |
about | string | Hub description | false |
tags | string | Comma separated tags | false |
Code | Description |
---|---|
42219001 | Body is missing required properties |
50019002 | Internal error - Apillon was unable to create hub. |
Response is an instance of hub class, described above.
Z)for(;R<=K;)He(d[R],L,S,!0),R++;else{const re=R,se=R,ye=new Map;for(R=se;R<=Z;R++){const Ge=p[R]=F?St(p[R]):tt(p[R]);Ge.key!=null&&ye.set(Ge.key,R)}let _e,Te=0;const et=Z-se+1;let en=!1,Yr=0;const yn=new Array(et);for(R=0;R=et){He(Ge,L,S,!0);continue}let st;if(Ge.key!=null)st=ye.get(Ge.key);else for(_e=se;_e<=Z;_e++)if(yn[_e-se]===0&&Vt(Ge,p[_e])){st=_e;break}st===void 0?He(Ge,L,S,!0):(yn[st-se]=R+1,st>=Yr?Yr=st:en=!0,k(Ge,p[st],g,null,L,S,z,$,F),Te++)}const Jr=en?Pc(yn):ln;for(_e=Jr.length-1,R=et-1;R>=0;R--){const Ge=se+R,st=p[Ge],Qr=Ge+1 {const{el:S,type:z,transition:$,children:F,shapeFlag:R}=d;if(R&6){Ve(d.component.subTree,p,g,w);return}if(R&128){d.suspense.move(p,g,w);return}if(R&64){z.move(d,p,g,M);return}if(z===ke){o(S,p,g);for(let K=0;K$.enter(S),L);else{const{leave:K,delayLeave:Z,afterLeave:re}=$,se=()=>o(S,p,g),ye=()=>{K(S,()=>{se(),re&&re()})};Z?Z(S,se,ye):ye()}else o(S,p,g)},He=(d,p,g,w=!1,L=!1)=>{const{type:S,props:z,ref:$,children:F,dynamicChildren:R,shapeFlag:Q,patchFlag:K,dirs:Z}=d;if($!=null&&ko($,null,g,d,!0),Q&256){p.ctx.deactivate(d);return}const re=Q&1&&Z,se=!un(d);let ye;if(se&&(ye=z&&z.onVnodeBeforeUnmount)&&Ze(ye,p,d),Q&6)it(d.component,g,w);else{if(Q&128){d.suspense.unmount(g,w);return}re&&at(d,null,p,"beforeUnmount"),Q&64?d.type.remove(d,p,g,L,M,w):R&&(S!==ke||K>0&&K&64)?Ne(R,p,g,!1,!0):(S===ke&&K&384||!L&&Q&16)&&Ne(F,p,g),w&&kt(d)}(se&&(ye=z&&z.onVnodeUnmounted)||re)&&We(()=>{ye&&Ze(ye,p,d),re&&at(d,null,p,"unmounted")},g)},kt=d=>{const{type:p,el:g,anchor:w,transition:L}=d;if(p===ke){Et(g,w);return}if(p===Sn){_(d);return}const S=()=>{r(g),L&&!L.persisted&&L.afterLeave&&L.afterLeave()};if(d.shapeFlag&1&&L&&!L.persisted){const{leave:z,delayLeave:$}=L,F=()=>z(g,S);$?$(d.el,S,F):F()}else S()},Et=(d,p)=>{let g;for(;d!==p;)g=h(d),r(d),d=g;r(p)},it=(d,p,g)=>{const{bum:w,scope:L,update:S,subTree:z,um:$}=d;w&&jo(w),L.stop(),S&&(S.active=!1,He(z,d,p,g)),$&&We($,p),We(()=>{d.isUnmounted=!0},p),p&&p.pendingBranch&&!p.isUnmounted&&d.asyncDep&&!d.asyncResolved&&d.suspenseId===p.pendingId&&(p.deps--,p.deps===0&&p.resolve())},Ne=(d,p,g,w=!1,L=!1,S=0)=>{for(let z=S;z d.shapeFlag&6?C(d.component.subTree):d.shapeFlag&128?d.suspense.next():h(d.anchor||d.el),V=(d,p,g)=>{d==null?p._vnode&&He(p._vnode,null,null,!0):k(p._vnode||null,d,p,null,null,null,g),sl(),vo(),p._vnode=d},M={p:k,um:He,m:Ve,r:kt,mt:ne,mc:b,pc:W,pbc:P,n:C,o:e};let G,ce;return t&&([G,ce]=t(M)),{render:V,hydrate:G,createApp:wc(V,G)}}function Ft({effect:e,update:t},n){e.allowRecurse=t.allowRecurse=n}function rs(e,t){return(!e||e&&!e.pendingBranch)&&t&&!t.persisted}function ls(e,t,n=!1){const o=e.children,r=t.children;if(X(o)&&X(r))for(let l=0;l >1,e[n[s]] 0&&(t[o]=n[l-1]),n[l]=o)}}for(l=n.length,i=n[l-1];l-- >0;)n[l]=i,i=t[i];return n}const Rc=e=>e.__isTeleport,ke=Symbol.for("v-fgt"),dn=Symbol.for("v-txt"),Je=Symbol.for("v-cmt"),Sn=Symbol.for("v-stc"),Pn=[];let ot=null;function B(e=!1){Pn.push(ot=e?null:[])}function Ic(){Pn.pop(),ot=Pn[Pn.length-1]||null}let Dn=1;function _l(e){Dn+=e}function is(e){return e.dynamicChildren=Dn>0?ot||ln:null,Ic(),Dn>0&&ot&&ot.push(e),e}function ee(e,t,n,o,r,l){return is(he(e,t,n,o,r,l,!0))}function Ae(e,t,n,o,r){return is(oe(e,t,n,o,r,!0))}function Eo(e){return e?e.__v_isVNode===!0:!1}function Vt(e,t){return e.type===t.type&&e.key===t.key}const Mo="__vInternal",ss=({key:e})=>e??null,fo=({ref:e,ref_key:t,ref_for:n})=>(typeof e=="number"&&(e=""+e),e!=null?me(e)||$e(e)||le(e)?{i:Fe,r:e,k:t,f:!!n}:e:null);function he(e,t=null,n=null,o=0,r=null,l=e===ke?0:1,i=!1,s=!1){const a={__v_isVNode:!0,__v_skip:!0,type:e,props:t,key:t&&ss(t),ref:t&&fo(t),scopeId:Bi,slotScopeIds:null,children:n,component:null,suspense:null,ssContent:null,ssFallback:null,dirs:null,transition:null,el:null,anchor:null,target:null,targetAnchor:null,staticCount:0,shapeFlag:l,patchFlag:o,dynamicProps:r,dynamicChildren:null,appContext:null,ctx:Fe};return s?(Or(a,n),l&128&&e.normalize(a)):n&&(a.shapeFlag|=me(n)?8:16),Dn>0&&!i&&ot&&(a.patchFlag>0||l&6)&&a.patchFlag!==32&&ot.push(a),a}const oe=Oc;function Oc(e,t=null,n=null,o=0,r=null,l=!1){if((!e||e===Qa)&&(e=Je),Eo(e)){const s=Dt(e,t,!0);return n&&Or(s,n),Dn>0&&!l&&ot&&(s.shapeFlag&6?ot[ot.indexOf(e)]=s:ot.push(s)),s.patchFlag|=-2,s}if(Wc(e)&&(e=e.__vccOpts),t){t=Nc(t);let{class:s,style:a}=t;s&&!me(s)&&(t.class=Ue(s)),Ce(a)&&(Ri(a)&&!X(a)&&(a=Ie({},a)),t.style=Bn(a))}const i=me(e)?1:Xa(e)?128:Rc(e)?64:Ce(e)?4:le(e)?2:0;return he(e,t,n,o,r,i,l,!0)}function Nc(e){return e?Ri(e)||Mo in e?Ie({},e):e:null}function Dt(e,t,n=!1){const{props:o,ref:r,patchFlag:l,children:i}=e,s=t?hr(o||{},t):o;return{__v_isVNode:!0,__v_skip:!0,type:e.type,props:s,key:s&&ss(s),ref:t&&t.ref?n&&r?X(r)?r.concat(fo(t)):[r,fo(t)]:fo(t):r,scopeId:e.scopeId,slotScopeIds:e.slotScopeIds,children:i,target:e.target,targetAnchor:e.targetAnchor,staticCount:e.staticCount,shapeFlag:e.shapeFlag,patchFlag:t&&e.type!==ke?l===-1?16:l|16:l,dynamicProps:e.dynamicProps,dynamicChildren:e.dynamicChildren,appContext:e.appContext,dirs:e.dirs,transition:e.transition,component:e.component,suspense:e.suspense,ssContent:e.ssContent&&Dt(e.ssContent),ssFallback:e.ssFallback&&Dt(e.ssFallback),el:e.el,anchor:e.anchor,ctx:e.ctx,ce:e.ce}}function Mt(e=" ",t=0){return oe(dn,null,e,t)}function $c(e,t){const n=oe(Sn,null,e);return n.staticCount=t,n}function Le(e="",t=!1){return t?(B(),Ae(Je,null,e)):oe(Je,null,e)}function tt(e){return e==null||typeof e=="boolean"?oe(Je):X(e)?oe(ke,null,e.slice()):typeof e=="object"?St(e):oe(dn,null,String(e))}function St(e){return e.el===null&&e.patchFlag!==-1||e.memo?e:Dt(e)}function Or(e,t){let n=0;const{shapeFlag:o}=e;if(t==null)t=null;else if(X(t))n=16;else if(typeof t=="object")if(o&65){const r=t.default;r&&(r._c&&(r._d=!1),Or(e,r()),r._c&&(r._d=!0));return}else{n=32;const r=t._;!r&&!(Mo in t)?t._ctx=Fe:r===3&&Fe&&(Fe.slots._===1?t._=1:(t._=2,e.patchFlag|=1024))}else le(t)?(t={default:t,_ctx:Fe},n=32):(t=String(t),o&64?(n=16,t=[Mt(t)]):n=8);e.children=t,e.shapeFlag|=n}function hr(...e){const t={};for(let n=0;n Pe||Fe;let $r,tn,yl="__VUE_INSTANCE_SETTERS__";(tn=nr()[yl])||(tn=nr()[yl]=[]),tn.push(e=>Pe=e),$r=e=>{tn.length>1?tn.forEach(t=>t(e)):tn[0](e)};const hn=e=>{$r(e),e.scope.on()},Gt=()=>{Pe&&Pe.scope.off(),$r(null)};function as(e){return e.vnode.shapeFlag&4}let pn=!1;function Hc(e,t=!1){pn=t;const{props:n,children:o}=e.vnode,r=as(e);kc(e,n,r,t),xc(e,o);const l=r?zc(e,t):void 0;return pn=!1,l}function zc(e,t){const n=e.type;e.accessCache=Object.create(null),e.proxy=Ii(new Proxy(e.ctx,pc));const{setup:o}=n;if(o){const r=e.setupContext=o.length>1?jc(e):null;hn(e),bn();const l=Ot(o,e,0,[e.props,r]);if(_n(),Gt(),pi(l)){if(l.then(Gt,Gt),t)return l.then(i=>{wl(e,i,t)}).catch(i=>{Wn(i,e,0)});e.asyncDep=l}else wl(e,l,t)}else cs(e,t)}function wl(e,t,n){le(t)?e.type.__ssrInlineRender?e.ssrRender=t:e.render=t:Ce(t)&&(e.setupState=Di(t)),cs(e,n)}let kl;function cs(e,t,n){const o=e.type;if(!e.render){if(!t&&kl&&!o.render){const r=o.template||Rr(e).template;if(r){const{isCustomElement:l,compilerOptions:i}=e.appContext.config,{delimiters:s,compilerOptions:a}=o,c=Ie(Ie({isCustomElement:l,delimiters:s},i),a);o.render=kl(r,c)}}e.render=o.render||rt}{hn(e),bn();try{mc(e)}finally{_n(),Gt()}}}function Bc(e){return e.attrsProxy||(e.attrsProxy=new Proxy(e.attrs,{get(t,n){return Ke(e,"get","$attrs"),t[n]}}))}function jc(e){const t=n=>{e.exposed=n||{}};return{get attrs(){return Bc(e)},slots:e.slots,emit:e.emit,expose:t}}function Fo(e){if(e.exposed)return e.exposeProxy||(e.exposeProxy=new Proxy(Di(Ii(e.exposed)),{get(t,n){if(n in t)return t[n];if(n in An)return An[n](e)},has(t,n){return n in t||n in An}}))}function Vc(e,t=!0){return le(e)?e.displayName||e.name:e.name||t&&e.__name}function Wc(e){return le(e)&&"__vccOpts"in e}const j=(e,t)=>za(e,t,pn);function ae(e,t,n){const o=arguments.length;return o===2?Ce(t)&&!X(t)?Eo(t)?oe(e,null,[t]):oe(e,t):oe(e,null,t):(o>3?n=Array.prototype.slice.call(arguments,2):o===3&&Eo(n)&&(n=[n]),oe(e,t,n))}const Uc=Symbol.for("v-scx"),Kc=()=>Se(Uc),qc="3.3.8",Gc="http://www.w3.org/2000/svg",Wt=typeof document<"u"?document:null,El=Wt&&Wt.createElement("template"),Yc={insert:(e,t,n)=>{t.insertBefore(e,n||null)},remove:e=>{const t=e.parentNode;t&&t.removeChild(e)},createElement:(e,t,n,o)=>{const r=t?Wt.createElementNS(Gc,e):Wt.createElement(e,n?{is:n}:void 0);return e==="select"&&o&&o.multiple!=null&&r.setAttribute("multiple",o.multiple),r},createText:e=>Wt.createTextNode(e),createComment:e=>Wt.createComment(e),setText:(e,t)=>{e.nodeValue=t},setElementText:(e,t)=>{e.textContent=t},parentNode:e=>e.parentNode,nextSibling:e=>e.nextSibling,querySelector:e=>Wt.querySelector(e),setScopeId(e,t){e.setAttribute(t,"")},insertStaticContent(e,t,n,o,r,l){const i=n?n.previousSibling:t.lastChild;if(r&&(r===l||r.nextSibling))for(;t.insertBefore(r.cloneNode(!0),n),!(r===l||!(r=r.nextSibling)););else{El.innerHTML=o?``:e;const s=El.content;if(o){const a=s.firstChild;for(;a.firstChild;)s.appendChild(a.firstChild);s.removeChild(a)}t.insertBefore(s,n)}return[i?i.nextSibling:t.firstChild,n?n.previousSibling:t.lastChild]}},Lt="transition",wn="animation",Mn=Symbol("_vtc"),Gn=(e,{slots:t})=>ae(oc,Jc(e),t);Gn.displayName="Transition";const us={name:String,type:String,css:{type:Boolean,default:!0},duration:[String,Number,Object],enterFromClass:String,enterActiveClass:String,enterToClass:String,appearFromClass:String,appearActiveClass:String,appearToClass:String,leaveFromClass:String,leaveActiveClass:String,leaveToClass:String};Gn.props=Ie({},Ki,us);const Ht=(e,t=[])=>{X(e)?e.forEach(n=>n(...t)):e&&e(...t)},Cl=e=>e?X(e)?e.some(t=>t.length>1):e.length>1:!1;function Jc(e){const t={};for(const E in e)E in us||(t[E]=e[E]);if(e.css===!1)return t;const{name:n="v",type:o,duration:r,enterFromClass:l=`${n}-enter-from`,enterActiveClass:i=`${n}-enter-active`,enterToClass:s=`${n}-enter-to`,appearFromClass:a=l,appearActiveClass:c=i,appearToClass:u=s,leaveFromClass:f=`${n}-leave-from`,leaveActiveClass:h=`${n}-leave-active`,leaveToClass:m=`${n}-leave-to`}=e,y=Qc(r),k=y&&y[0],x=y&&y[1],{onBeforeEnter:T,onEnter:A,onEnterCancelled:v,onLeave:_,onLeaveCancelled:H,onBeforeAppear:q=T,onAppear:D=A,onAppearCancelled:b=v}=t,O=(E,N,ne)=>{zt(E,N?u:s),zt(E,N?c:i),ne&&ne()},P=(E,N)=>{E._isLeaving=!1,zt(E,f),zt(E,m),zt(E,h),N&&N()},U=E=>(N,ne)=>{const ie=E?D:A,I=()=>O(N,E,ne);Ht(ie,[N,I]),xl(()=>{zt(N,E?a:l),Tt(N,E?u:s),Cl(ie)||Ll(N,o,k,I)})};return Ie(t,{onBeforeEnter(E){Ht(T,[E]),Tt(E,l),Tt(E,i)},onBeforeAppear(E){Ht(q,[E]),Tt(E,a),Tt(E,c)},onEnter:U(!1),onAppear:U(!0),onLeave(E,N){E._isLeaving=!0;const ne=()=>P(E,N);Tt(E,f),eu(),Tt(E,h),xl(()=>{E._isLeaving&&(zt(E,f),Tt(E,m),Cl(_)||Ll(E,o,x,ne))}),Ht(_,[E,ne])},onEnterCancelled(E){O(E,!1),Ht(v,[E])},onAppearCancelled(E){O(E,!0),Ht(b,[E])},onLeaveCancelled(E){P(E),Ht(H,[E])}})}function Qc(e){if(e==null)return null;if(Ce(e))return[qo(e.enter),qo(e.leave)];{const t=qo(e);return[t,t]}}function qo(e){return ra(e)}function Tt(e,t){t.split(/\s+/).forEach(n=>n&&e.classList.add(n)),(e[Mn]||(e[Mn]=new Set)).add(t)}function zt(e,t){t.split(/\s+/).forEach(o=>o&&e.classList.remove(o));const n=e[Mn];n&&(n.delete(t),n.size||(e[Mn]=void 0))}function xl(e){requestAnimationFrame(()=>{requestAnimationFrame(e)})}let Zc=0;function Ll(e,t,n,o){const r=e._endId=++Zc,l=()=>{r===e._endId&&o()};if(n)return setTimeout(l,n);const{type:i,timeout:s,propCount:a}=Xc(e,t);if(!i)return o();const c=i+"end";let u=0;const f=()=>{e.removeEventListener(c,h),l()},h=m=>{m.target===e&&++u>=a&&f()};setTimeout(()=>{u(n[y]||"").split(", "),r=o(`${Lt}Delay`),l=o(`${Lt}Duration`),i=Tl(r,l),s=o(`${wn}Delay`),a=o(`${wn}Duration`),c=Tl(s,a);let u=null,f=0,h=0;t===Lt?i>0&&(u=Lt,f=i,h=l.length):t===wn?c>0&&(u=wn,f=c,h=a.length):(f=Math.max(i,c),u=f>0?i>c?Lt:wn:null,h=u?u===Lt?l.length:a.length:0);const m=u===Lt&&/\b(transform|all)(,|$)/.test(o(`${Lt}Property`).toString());return{type:u,timeout:f,propCount:h,hasTransform:m}}function Tl(e,t){for(;e.length Al(n)+Al(e[o])))}function Al(e){return e==="auto"?0:Number(e.slice(0,-1).replace(",","."))*1e3}function eu(){return document.body.offsetHeight}function tu(e,t,n){const o=e[Mn];o&&(t=(t?[t,...o]:[...o]).join(" ")),t==null?e.removeAttribute("class"):n?e.setAttribute("class",t):e.className=t}const Dr=Symbol("_vod"),Co={beforeMount(e,{value:t},{transition:n}){e[Dr]=e.style.display==="none"?"":e.style.display,n&&t?n.beforeEnter(e):kn(e,t)},mounted(e,{value:t},{transition:n}){n&&t&&n.enter(e)},updated(e,{value:t,oldValue:n},{transition:o}){!t!=!n&&(o?t?(o.beforeEnter(e),kn(e,!0),o.enter(e)):o.leave(e,()=>{kn(e,!1)}):kn(e,t))},beforeUnmount(e,{value:t}){kn(e,t)}};function kn(e,t){e.style.display=t?e[Dr]:"none"}function nu(e,t,n){const o=e.style,r=me(n);if(n&&!r){if(t&&!me(t))for(const l in t)n[l]==null&&pr(o,l,"");for(const l in n)pr(o,l,n[l])}else{const l=o.display;r?t!==n&&(o.cssText=n):t&&e.removeAttribute("style"),Dr in e&&(o.display=l)}}const Sl=/\s*!important$/;function pr(e,t,n){if(X(n))n.forEach(o=>pr(e,t,o));else if(n==null&&(n=""),t.startsWith("--"))e.setProperty(t,n);else{const o=ou(e,t);Sl.test(n)?e.setProperty(Qt(o),n.replace(Sl,""),"important"):e[o]=n}}const Pl=["Webkit","Moz","ms"],Go={};function ou(e,t){const n=Go[t];if(n)return n;let o=ft(t);if(o!=="filter"&&o in e)return Go[t]=o;o=Po(o);for(let r=0;r Yo||(uu.then(()=>Yo=0),Yo=Date.now());function du(e,t){const n=o=>{if(!o._vts)o._vts=Date.now();else if(o._vts<=n.attached)return;Xe(hu(o,n.value),t,5,[o])};return n.value=e,n.attached=fu(),n}function hu(e,t){if(X(t)){const n=e.stopImmediatePropagation;return e.stopImmediatePropagation=()=>{n.call(e),e._stopped=!0},t.map(o=>r=>!r._stopped&&o&&o(r))}else return t}const Nl=/^on[a-z]/,pu=(e,t,n,o,r=!1,l,i,s,a)=>{t==="class"?tu(e,o,r):t==="style"?nu(e,n,o):zn(t)?_r(t)||au(e,t,n,o,i):(t[0]==="."?(t=t.slice(1),!0):t[0]==="^"?(t=t.slice(1),!1):mu(e,t,o,r))?lu(e,t,o,l,i,s,a):(t==="true-value"?e._trueValue=o:t==="false-value"&&(e._falseValue=o),ru(e,t,o,r))};function mu(e,t,n,o){return o?!!(t==="innerHTML"||t==="textContent"||t in e&&Nl.test(t)&&le(n)):t==="spellcheck"||t==="draggable"||t==="translate"||t==="form"||t==="list"&&e.tagName==="INPUT"||t==="type"&&e.tagName==="TEXTAREA"||Nl.test(t)&&me(n)?!1:t in e}const gu={esc:"escape",space:" ",up:"arrow-up",left:"arrow-left",right:"arrow-right",down:"arrow-down",delete:"backspace"},vu=(e,t)=>n=>{if(!("key"in n))return;const o=Qt(n.key);if(t.some(r=>r===o||gu[r]===o))return e(n)},bu=Ie({patchProp:pu},Yc);let Jo,$l=!1;function _u(){return Jo=$l?Jo:Ac(bu),$l=!0,Jo}const yu=(...e)=>{const t=_u().createApp(...e),{mount:n}=t;return t.mount=o=>{const r=wu(o);if(r)return n(r,!0,r instanceof SVGElement)},t};function wu(e){return me(e)?document.querySelector(e):e}const ku={"v-8daa1a0e":()=>Y(()=>import("./index.html-d0c26fab.js"),[]).then(({data:e})=>e),"v-8daa1a0e":()=>Y(()=>import("./index.html-d0c26fab.js"),[]).then(({data:e})=>e),"v-92c536e4":()=>Y(()=>import("./1-navigation.html-01a6a8ce.js"),[]).then(({data:e})=>e),"v-d64fcca0":()=>Y(()=>import("./2-what-is-apillon.html-23c88ace.js"),[]).then(({data:e})=>e),"v-0f48940c":()=>Y(()=>import("./3-why-apillon.html-22704871.js"),[]).then(({data:e})=>e),"v-6bda2dde":()=>Y(()=>import("./4-how-does-apillon-work.html-df2eb8bd.js"),[]).then(({data:e})=>e),"v-ac15b5a4":()=>Y(()=>import("./5-developing-web3-with-apillon.html-eb7334b6.js"),[]).then(({data:e})=>e),"v-da41cf52":()=>Y(()=>import("./6-web3-up-close.html-77cdd341.js"),[]).then(({data:e})=>e),"v-929e013e":()=>Y(()=>import("./7-community.html-26fa6d30.js"),[]).then(({data:e})=>e),"v-564790ea":()=>Y(()=>import("./8-nctr-token.html-c41c7f8e.js"),[]).then(({data:e})=>e),"v-cdfbcdae":()=>Y(()=>import("./1-apillon-api.html-83f1e138.js"),[]).then(({data:e})=>e),"v-4d91ab64":()=>Y(()=>import("./10-flutter-sdk.html-19b7bbda.js"),[]).then(({data:e})=>e),"v-1da918f4":()=>Y(()=>import("./2-storage-api.html-db7f555d.js"),[]).then(({data:e})=>e),"v-3fe44474":()=>Y(()=>import("./3-hosting-api.html-6ebd1a24.js"),[]).then(({data:e})=>e),"v-ae3a6dfc":()=>Y(()=>import("./4-nfts-api.html-6338f0c4.js"),[]).then(({data:e})=>e),"v-fc8498f6":()=>Y(()=>import("./5-apillon-sdk.html-12a9e833.js"),[]).then(({data:e})=>e),"v-6c04f07e":()=>Y(()=>import("./6-apillon-cli.html-1a4cac9c.js"),[]).then(({data:e})=>e),"v-15f6e7df":()=>Y(()=>import("./7-apillon-oauth-integration.html-05185b5a.js"),[]).then(({data:e})=>e),"v-6b6a7fe7":()=>Y(()=>import("./8-computing-api.html-9982cda6.js"),[]).then(({data:e})=>e),"v-6ef47b51":()=>Y(()=>import("./9-social-api.html-310adc87.js"),[]).then(({data:e})=>e),"v-2d5fd574":()=>Y(()=>import("./1-good-to-know.html-5baf020d.js"),[]).then(({data:e})=>e),"v-47991eb5":()=>Y(()=>import("./2-web3-storage.html-16638133.js"),[]).then(({data:e})=>e),"v-f2d17dd2":()=>Y(()=>import("./3-web3-hosting.html-91c06423.js"),[]).then(({data:e})=>e),"v-713d4f4d":()=>Y(()=>import("./4-nfts.html-dbff7a3f.js"),[]).then(({data:e})=>e),"v-662ed367":()=>Y(()=>import("./5-web3-authentication.html-fc5dcfac.js"),[]).then(({data:e})=>e),"v-592c909e":()=>Y(()=>import("./6-web3-social.html-b71e29ed.js"),[]).then(({data:e})=>e),"v-b6f01184":()=>Y(()=>import("./7-web3-compute.html-e6244f58.js"),[]).then(({data:e})=>e),"v-3706649a":()=>Y(()=>import("./404.html-60b35caa.js"),[]).then(({data:e})=>e)},Eu=JSON.parse('{"base":"/","lang":"en-US","title":"Apillon Wiki","description":"Apillon Wiki","head":[["link",{"rel":"icon","href":"/assets/logo-favicon.png","sizes":"32x32"}],["link",{"rel":"icon","href":"/assets/logo-favicon.png","sizes":"192x192"}],["link",{"rel":"apple-touch-icon","href":"/assets/logo-favicon.png"}],["meta",{"name":"msapplication-TileImage","content":"/assets/logo-favicon.png"}]],"locales":{}}');var Cu=([e,t,n])=>e==="meta"&&t.name?`${e}.${t.name}`:["title","base"].includes(e)?e:e==="template"&&t.id?`${e}.${t.id}`:JSON.stringify([e,t,n]),xu=e=>{const t=new Set,n=[];return e.forEach(o=>{const r=Cu(o);t.has(r)||(t.add(r),n.push(o))}),n},Yn=e=>/^(https?:)?\/\//.test(e),Lu=e=>/^mailto:/.test(e),Tu=e=>/^tel:/.test(e),Mr=e=>Object.prototype.toString.call(e)==="[object Object]",fs=e=>e[e.length-1]==="/"?e.slice(0,-1):e,ds=e=>e[0]==="/"?e.slice(1):e,hs=(e,t)=>{const n=Object.keys(e).sort((o,r)=>{const l=r.split("/").length-o.split("/").length;return l!==0?l:r.length-o.length});for(const o of n)if(t.startsWith(o))return o;return"/"};const ps={"v-8daa1a0e":ge(()=>Y(()=>import("./index.html-7df57863.js"),[])),"v-8daa1a0e":ge(()=>Y(()=>import("./index.html-7df57863.js"),[])),"v-92c536e4":ge(()=>Y(()=>import("./1-navigation.html-d37240e3.js"),[])),"v-d64fcca0":ge(()=>Y(()=>import("./2-what-is-apillon.html-65077af6.js"),[])),"v-0f48940c":ge(()=>Y(()=>import("./3-why-apillon.html-f7d508e5.js"),[])),"v-6bda2dde":ge(()=>Y(()=>import("./4-how-does-apillon-work.html-9f4211d2.js"),[])),"v-ac15b5a4":ge(()=>Y(()=>import("./5-developing-web3-with-apillon.html-7d538dd0.js"),[])),"v-da41cf52":ge(()=>Y(()=>import("./6-web3-up-close.html-a27998e5.js"),[])),"v-929e013e":ge(()=>Y(()=>import("./7-community.html-375ba728.js"),[])),"v-564790ea":ge(()=>Y(()=>import("./8-nctr-token.html-62d0c7cb.js"),[])),"v-cdfbcdae":ge(()=>Y(()=>import("./1-apillon-api.html-70dd4689.js"),[])),"v-4d91ab64":ge(()=>Y(()=>import("./10-flutter-sdk.html-909a04d0.js"),[])),"v-1da918f4":ge(()=>Y(()=>import("./2-storage-api.html-f39db820.js"),[])),"v-3fe44474":ge(()=>Y(()=>import("./3-hosting-api.html-e0ef4a3a.js"),[])),"v-ae3a6dfc":ge(()=>Y(()=>import("./4-nfts-api.html-3effba48.js"),[])),"v-fc8498f6":ge(()=>Y(()=>import("./5-apillon-sdk.html-52249e73.js"),[])),"v-6c04f07e":ge(()=>Y(()=>import("./6-apillon-cli.html-63ac0430.js"),[])),"v-15f6e7df":ge(()=>Y(()=>import("./7-apillon-oauth-integration.html-83981cb2.js"),[])),"v-6b6a7fe7":ge(()=>Y(()=>import("./8-computing-api.html-6e253551.js"),[])),"v-6ef47b51":ge(()=>Y(()=>import("./9-social-api.html-010a3fe5.js"),[])),"v-2d5fd574":ge(()=>Y(()=>import("./1-good-to-know.html-9b77b1b2.js"),[])),"v-47991eb5":ge(()=>Y(()=>import("./2-web3-storage.html-8dffede4.js"),[])),"v-f2d17dd2":ge(()=>Y(()=>import("./3-web3-hosting.html-10d185fd.js"),[])),"v-713d4f4d":ge(()=>Y(()=>import("./4-nfts.html-ba4ddb92.js"),[])),"v-662ed367":ge(()=>Y(()=>import("./5-web3-authentication.html-99f98d12.js"),[])),"v-592c909e":ge(()=>Y(()=>import("./6-web3-social.html-c81f1620.js"),[])),"v-b6f01184":ge(()=>Y(()=>import("./7-web3-compute.html-08dd6fee.js"),[])),"v-3706649a":ge(()=>Y(()=>import("./404.html-63068049.js"),[]))};var Au=Symbol(""),Su=be(ku),ms=Vn({key:"",path:"",title:"",lang:"",frontmatter:{},headers:[]}),Pt=be(ms),Yt=()=>Pt,gs=Symbol(""),vt=()=>{const e=Se(gs);if(!e)throw new Error("usePageFrontmatter() is called without provider.");return e},vs=Symbol(""),Pu=()=>{const e=Se(vs);if(!e)throw new Error("usePageHead() is called without provider.");return e},Ru=Symbol(""),bs=Symbol(""),Iu=()=>{const e=Se(bs);if(!e)throw new Error("usePageLang() is called without provider.");return e},_s=Symbol(""),Ou=()=>{const e=Se(_s);if(!e)throw new Error("usePageLayout() is called without provider.");return e},Fr=Symbol(""),Jn=()=>{const e=Se(Fr);if(!e)throw new Error("useRouteLocale() is called without provider.");return e},rn=be(Eu),ys=()=>rn,ws=Symbol(""),Hr=()=>{const e=Se(ws);if(!e)throw new Error("useSiteLocaleData() is called without provider.");return e},Nu=Symbol(""),$u="Layout",Du="NotFound",ht=jn({resolveLayouts:e=>e.reduce((t,n)=>({...t,...n.layouts}),{}),resolvePageData:async e=>{const t=Su.value[e];return await(t==null?void 0:t())??ms},resolvePageFrontmatter:e=>e.frontmatter,resolvePageHead:(e,t,n)=>{const o=me(t.description)?t.description:n.description,r=[...X(t.head)?t.head:[],...n.head,["title",{},e],["meta",{name:"description",content:o}]];return xu(r)},resolvePageHeadTitle:(e,t)=>[e.title,t.title].filter(n=>!!n).join(" | "),resolvePageLang:(e,t)=>e.lang||t.lang||"en-US",resolvePageLayout:(e,t)=>{let n;if(e.path){const o=e.frontmatter.layout;me(o)?n=o:n=$u}else n=Du;return t[n]},resolveRouteLocale:(e,t)=>hs(e,t),resolveSiteLocaleData:(e,t)=>({...e,...e.locales[t]})}),zr=ue({name:"ClientOnly",setup(e,t){const n=be(!1);return qe(()=>{n.value=!0}),()=>{var o,r;return n.value?(r=(o=t.slots).default)==null?void 0:r.call(o):null}}}),Mu=ue({name:"Content",props:{pageKey:{type:String,required:!1,default:""}},setup(e){const t=Yt(),n=j(()=>ps[e.pageKey||t.value.key]);return()=>n.value?ae(n.value):ae("div","404 Not Found")}}),wt=(e={})=>e,Br=e=>Yn(e)?e:`/${ds(e)}`;function ks(e,t,n){var o,r,l;t===void 0&&(t=50),n===void 0&&(n={});var i=(o=n.isImmediate)!=null&&o,s=(r=n.callback)!=null&&r,a=n.maxWait,c=Date.now(),u=[];function f(){if(a!==void 0){var m=Date.now()-c;if(m+t>=a)return a-m}return t}var h=function(){var m=[].slice.call(arguments),y=this;return new Promise(function(k,x){var T=i&&l===void 0;if(l!==void 0&&clearTimeout(l),l=setTimeout(function(){if(l=void 0,c=Date.now(),!i){var v=e.apply(y,m);s&&s(v),u.forEach(function(_){return(0,_.resolve)(v)}),u=[]}},f()),T){var A=e.apply(y,m);return s&&s(A),k(A)}u.push({resolve:k,reject:x})})};return h.cancel=function(m){l!==void 0&&clearTimeout(l),u.forEach(function(y){return(0,y.reject)(m)}),u=[]},h}/*! + * vue-router v4.2.5 + * (c) 2023 Eduardo San Martin Morote + * @license MIT + */const on=typeof window<"u";function Fu(e){return e.__esModule||e[Symbol.toStringTag]==="Module"}const ve=Object.assign;function Qo(e,t){const n={};for(const o in t){const r=t[o];n[o]=lt(r)?r.map(e):e(r)}return n}const Rn=()=>{},lt=Array.isArray,Hu=/\/$/,zu=e=>e.replace(Hu,"");function Zo(e,t,n="/"){let o,r={},l="",i="";const s=t.indexOf("#");let a=t.indexOf("?");return s=0&&(a=-1),a>-1&&(o=t.slice(0,a),l=t.slice(a+1,s>-1?s:t.length),r=e(l)),s>-1&&(o=o||t.slice(0,s),i=t.slice(s,t.length)),o=Wu(o??t,n),{fullPath:o+(l&&"?")+l+i,path:o,query:r,hash:i}}function Bu(e,t){const n=t.query?e(t.query):"";return t.path+(n&&"?")+n+(t.hash||"")}function Dl(e,t){return!t||!e.toLowerCase().startsWith(t.toLowerCase())?e:e.slice(t.length)||"/"}function ju(e,t,n){const o=t.matched.length-1,r=n.matched.length-1;return o>-1&&o===r&&mn(t.matched[o],n.matched[r])&&Es(t.params,n.params)&&e(t.query)===e(n.query)&&t.hash===n.hash}function mn(e,t){return(e.aliasOf||e)===(t.aliasOf||t)}function Es(e,t){if(Object.keys(e).length!==Object.keys(t).length)return!1;for(const n in e)if(!Vu(e[n],t[n]))return!1;return!0}function Vu(e,t){return lt(e)?Ml(e,t):lt(t)?Ml(t,e):e===t}function Ml(e,t){return lt(t)?e.length===t.length&&e.every((n,o)=>n===t[o]):e.length===1&&e[0]===t}function Wu(e,t){if(e.startsWith("/"))return e;if(!e)return t;const n=t.split("/"),o=e.split("/"),r=o[o.length-1];(r===".."||r===".")&&o.push("");let l=n.length-1,i,s;for(i=0;i 1&&l--;else break;return n.slice(0,l).join("/")+"/"+o.slice(i-(i===o.length?1:0)).join("/")}var Fn;(function(e){e.pop="pop",e.push="push"})(Fn||(Fn={}));var In;(function(e){e.back="back",e.forward="forward",e.unknown=""})(In||(In={}));function Uu(e){if(!e)if(on){const t=document.querySelector("base");e=t&&t.getAttribute("href")||"/",e=e.replace(/^\w+:\/\/[^\/]+/,"")}else e="/";return e[0]!=="/"&&e[0]!=="#"&&(e="/"+e),zu(e)}const Ku=/^[^#]+#/;function qu(e,t){return e.replace(Ku,"#")+t}function Gu(e,t){const n=document.documentElement.getBoundingClientRect(),o=e.getBoundingClientRect();return{behavior:t.behavior,left:o.left-n.left-(t.left||0),top:o.top-n.top-(t.top||0)}}const Ho=()=>({left:window.pageXOffset,top:window.pageYOffset});function Yu(e){let t;if("el"in e){const n=e.el,o=typeof n=="string"&&n.startsWith("#"),r=typeof n=="string"?o?document.getElementById(n.slice(1)):document.querySelector(n):n;if(!r)return;t=Gu(r,e)}else t=e;"scrollBehavior"in document.documentElement.style?window.scrollTo(t):window.scrollTo(t.left!=null?t.left:window.pageXOffset,t.top!=null?t.top:window.pageYOffset)}function Fl(e,t){return(history.state?history.state.position-t:-1)+e}const mr=new Map;function Ju(e,t){mr.set(e,t)}function Qu(e){const t=mr.get(e);return mr.delete(e),t}let Zu=()=>location.protocol+"//"+location.host;function Cs(e,t){const{pathname:n,search:o,hash:r}=t,l=e.indexOf("#");if(l>-1){let s=r.includes(e.slice(l))?e.slice(l).length:1,a=r.slice(s);return a[0]!=="/"&&(a="/"+a),Dl(a,"")}return Dl(n,e)+o+r}function Xu(e,t,n,o){let r=[],l=[],i=null;const s=({state:h})=>{const m=Cs(e,location),y=n.value,k=t.value;let x=0;if(h){if(n.value=m,t.value=h,i&&i===y){i=null;return}x=k?h.position-k.position:0}else o(m);r.forEach(T=>{T(n.value,y,{delta:x,type:Fn.pop,direction:x?x>0?In.forward:In.back:In.unknown})})};function a(){i=n.value}function c(h){r.push(h);const m=()=>{const y=r.indexOf(h);y>-1&&r.splice(y,1)};return l.push(m),m}function u(){const{history:h}=window;h.state&&h.replaceState(ve({},h.state,{scroll:Ho()}),"")}function f(){for(const h of l)h();l=[],window.removeEventListener("popstate",s),window.removeEventListener("beforeunload",u)}return window.addEventListener("popstate",s),window.addEventListener("beforeunload",u,{passive:!0}),{pauseListeners:a,listen:c,destroy:f}}function Hl(e,t,n,o=!1,r=!1){return{back:e,current:t,forward:n,replaced:o,position:window.history.length,scroll:r?Ho():null}}function ef(e){const{history:t,location:n}=window,o={value:Cs(e,n)},r={value:t.state};r.value||l(o.value,{back:null,current:o.value,forward:null,position:t.length-1,replaced:!0,scroll:null},!0);function l(a,c,u){const f=e.indexOf("#"),h=f>-1?(n.host&&document.querySelector("base")?e:e.slice(f))+a:Zu()+e+a;try{t[u?"replaceState":"pushState"](c,"",h),r.value=c}catch(m){console.error(m),n[u?"replace":"assign"](h)}}function i(a,c){const u=ve({},t.state,Hl(r.value.back,a,r.value.forward,!0),c,{position:r.value.position});l(a,u,!0),o.value=a}function s(a,c){const u=ve({},r.value,t.state,{forward:a,scroll:Ho()});l(u.current,u,!0);const f=ve({},Hl(o.value,a,null),{position:u.position+1},c);l(a,f,!1),o.value=a}return{location:o,state:r,push:s,replace:i}}function tf(e){e=Uu(e);const t=ef(e),n=Xu(e,t.state,t.location,t.replace);function o(l,i=!0){i||n.pauseListeners(),history.go(l)}const r=ve({location:"",base:e,go:o,createHref:qu.bind(null,e)},t,n);return Object.defineProperty(r,"location",{enumerable:!0,get:()=>t.location.value}),Object.defineProperty(r,"state",{enumerable:!0,get:()=>t.state.value}),r}function nf(e){return typeof e=="string"||e&&typeof e=="object"}function xs(e){return typeof e=="string"||typeof e=="symbol"}const pt={path:"/",name:void 0,params:{},query:{},hash:"",fullPath:"/",matched:[],meta:{},redirectedFrom:void 0},Ls=Symbol("");var zl;(function(e){e[e.aborted=4]="aborted",e[e.cancelled=8]="cancelled",e[e.duplicated=16]="duplicated"})(zl||(zl={}));function gn(e,t){return ve(new Error,{type:e,[Ls]:!0},t)}function dt(e,t){return e instanceof Error&&Ls in e&&(t==null||!!(e.type&t))}const Bl="[^/]+?",of={sensitive:!1,strict:!1,start:!0,end:!0},rf=/[.+*?^${}()[\]/\\]/g;function lf(e,t){const n=ve({},of,t),o=[];let r=n.start?"^":"";const l=[];for(const c of e){const u=c.length?[]:[90];n.strict&&!c.length&&(r+="/");for(let f=0;f t.length?t.length===1&&t[0]===40+40?1:-1:0}function af(e,t){let n=0;const o=e.score,r=t.score;for(;n 0&&t[t.length-1]<0}const cf={type:0,value:""},uf=/[a-zA-Z0-9_]/;function ff(e){if(!e)return[[]];if(e==="/")return[[cf]];if(!e.startsWith("/"))throw new Error(`Invalid path "${e}"`);function t(m){throw new Error(`ERR (${n})/"${c}": ${m}`)}let n=0,o=n;const r=[];let l;function i(){l&&r.push(l),l=[]}let s=0,a,c="",u="";function f(){c&&(n===0?l.push({type:0,value:c}):n===1||n===2||n===3?(l.length>1&&(a==="*"||a==="+")&&t(`A repeatable param (${c}) must be alone in its segment. eg: '/:ids+.`),l.push({type:1,value:c,regexp:u,repeatable:a==="*"||a==="+",optional:a==="*"||a==="?"})):t("Invalid state to consume buffer"),c="")}function h(){c+=a}for(;s {i(A)}:Rn}function i(u){if(xs(u)){const f=o.get(u);f&&(o.delete(u),n.splice(n.indexOf(f),1),f.children.forEach(i),f.alias.forEach(i))}else{const f=n.indexOf(u);f>-1&&(n.splice(f,1),u.record.name&&o.delete(u.record.name),u.children.forEach(i),u.alias.forEach(i))}}function s(){return n}function a(u){let f=0;for(;f =0&&(u.record.path!==n[f].record.path||!Ts(u,n[f]));)f++;n.splice(f,0,u),u.record.name&&!Wl(u)&&o.set(u.record.name,u)}function c(u,f){let h,m={},y,k;if("name"in u&&u.name){if(h=o.get(u.name),!h)throw gn(1,{location:u});k=h.record.name,m=ve(Vl(f.params,h.keys.filter(A=>!A.optional).map(A=>A.name)),u.params&&Vl(u.params,h.keys.map(A=>A.name))),y=h.stringify(m)}else if("path"in u)y=u.path,h=n.find(A=>A.re.test(y)),h&&(m=h.parse(y),k=h.record.name);else{if(h=f.name?o.get(f.name):n.find(A=>A.re.test(f.path)),!h)throw gn(1,{location:u,currentLocation:f});k=h.record.name,m=ve({},f.params,u.params),y=h.stringify(m)}const x=[];let T=h;for(;T;)x.unshift(T.record),T=T.parent;return{name:k,path:y,params:m,matched:x,meta:gf(x)}}return e.forEach(u=>l(u)),{addRoute:l,resolve:c,removeRoute:i,getRoutes:s,getRecordMatcher:r}}function Vl(e,t){const n={};for(const o of t)o in e&&(n[o]=e[o]);return n}function pf(e){return{path:e.path,redirect:e.redirect,name:e.name,meta:e.meta||{},aliasOf:void 0,beforeEnter:e.beforeEnter,props:mf(e),children:e.children||[],instances:{},leaveGuards:new Set,updateGuards:new Set,enterCallbacks:{},components:"components"in e?e.components||null:e.component&&{default:e.component}}}function mf(e){const t={},n=e.props||!1;if("component"in e)t.default=n;else for(const o in e.components)t[o]=typeof n=="object"?n[o]:n;return t}function Wl(e){for(;e;){if(e.record.aliasOf)return!0;e=e.parent}return!1}function gf(e){return e.reduce((t,n)=>ve(t,n.meta),{})}function Ul(e,t){const n={};for(const o in e)n[o]=o in t?t[o]:e[o];return n}function Ts(e,t){return t.children.some(n=>n===e||Ts(e,n))}const As=/#/g,vf=/&/g,bf=/\//g,_f=/=/g,yf=/\?/g,Ss=/\+/g,wf=/%5B/g,kf=/%5D/g,Ps=/%5E/g,Ef=/%60/g,Rs=/%7B/g,Cf=/%7C/g,Is=/%7D/g,xf=/%20/g;function jr(e){return encodeURI(""+e).replace(Cf,"|").replace(wf,"[").replace(kf,"]")}function Lf(e){return jr(e).replace(Rs,"{").replace(Is,"}").replace(Ps,"^")}function gr(e){return jr(e).replace(Ss,"%2B").replace(xf,"+").replace(As,"%23").replace(vf,"%26").replace(Ef,"`").replace(Rs,"{").replace(Is,"}").replace(Ps,"^")}function Tf(e){return gr(e).replace(_f,"%3D")}function Af(e){return jr(e).replace(As,"%23").replace(yf,"%3F")}function Sf(e){return e==null?"":Af(e).replace(bf,"%2F")}function xo(e){try{return decodeURIComponent(""+e)}catch{}return""+e}function Pf(e){const t={};if(e===""||e==="?")return t;const o=(e[0]==="?"?e.slice(1):e).split("&");for(let r=0;r l&&gr(l)):[o&&gr(o)]).forEach(l=>{l!==void 0&&(t+=(t.length?"&":"")+n,l!=null&&(t+="="+l))})}return t}function Rf(e){const t={};for(const n in e){const o=e[n];o!==void 0&&(t[n]=lt(o)?o.map(r=>r==null?null:""+r):o==null?o:""+o)}return t}const If=Symbol(""),ql=Symbol(""),zo=Symbol(""),Vr=Symbol(""),vr=Symbol("");function En(){let e=[];function t(o){return e.push(o),()=>{const r=e.indexOf(o);r>-1&&e.splice(r,1)}}function n(){e=[]}return{add:t,list:()=>e.slice(),reset:n}}function Rt(e,t,n,o,r){const l=o&&(o.enterCallbacks[r]=o.enterCallbacks[r]||[]);return()=>new Promise((i,s)=>{const a=f=>{f===!1?s(gn(4,{from:n,to:t})):f instanceof Error?s(f):nf(f)?s(gn(2,{from:t,to:f})):(l&&o.enterCallbacks[r]===l&&typeof f=="function"&&l.push(f),i())},c=e.call(o&&o.instances[r],t,n,a);let u=Promise.resolve(c);e.length<3&&(u=u.then(a)),u.catch(f=>s(f))})}function Xo(e,t,n,o){const r=[];for(const l of e)for(const i in l.components){let s=l.components[i];if(!(t!=="beforeRouteEnter"&&!l.instances[i]))if(Of(s)){const c=(s.__vccOpts||s)[t];c&&r.push(Rt(c,n,o,l,i))}else{let a=s();r.push(()=>a.then(c=>{if(!c)return Promise.reject(new Error(`Couldn't resolve component "${i}" at "${l.path}"`));const u=Fu(c)?c.default:c;l.components[i]=u;const h=(u.__vccOpts||u)[t];return h&&Rt(h,n,o,l,i)()}))}}return r}function Of(e){return typeof e=="object"||"displayName"in e||"props"in e||"__vccOpts"in e}function Gl(e){const t=Se(zo),n=Se(Vr),o=j(()=>t.resolve(te(e.to))),r=j(()=>{const{matched:a}=o.value,{length:c}=a,u=a[c-1],f=n.matched;if(!u||!f.length)return-1;const h=f.findIndex(mn.bind(null,u));if(h>-1)return h;const m=Yl(a[c-2]);return c>1&&Yl(u)===m&&f[f.length-1].path!==m?f.findIndex(mn.bind(null,a[c-2])):h}),l=j(()=>r.value>-1&&Mf(n.params,o.value.params)),i=j(()=>r.value>-1&&r.value===n.matched.length-1&&Es(n.params,o.value.params));function s(a={}){return Df(a)?t[te(e.replace)?"replace":"push"](te(e.to)).catch(Rn):Promise.resolve()}return{route:o,href:j(()=>o.value.href),isActive:l,isExactActive:i,navigate:s}}const Nf=ue({name:"RouterLink",compatConfig:{MODE:3},props:{to:{type:[String,Object],required:!0},replace:Boolean,activeClass:String,exactActiveClass:String,custom:Boolean,ariaCurrentValue:{type:String,default:"page"}},useLink:Gl,setup(e,{slots:t}){const n=jn(Gl(e)),{options:o}=Se(zo),r=j(()=>({[Jl(e.activeClass,o.linkActiveClass,"router-link-active")]:n.isActive,[Jl(e.exactActiveClass,o.linkExactActiveClass,"router-link-exact-active")]:n.isExactActive}));return()=>{const l=t.default&&t.default(n);return e.custom?l:ae("a",{"aria-current":n.isExactActive?e.ariaCurrentValue:null,href:n.href,onClick:n.navigate,class:r.value},l)}}}),$f=Nf;function Df(e){if(!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)&&!e.defaultPrevented&&!(e.button!==void 0&&e.button!==0)){if(e.currentTarget&&e.currentTarget.getAttribute){const t=e.currentTarget.getAttribute("target");if(/\b_blank\b/i.test(t))return}return e.preventDefault&&e.preventDefault(),!0}}function Mf(e,t){for(const n in t){const o=t[n],r=e[n];if(typeof o=="string"){if(o!==r)return!1}else if(!lt(r)||r.length!==o.length||o.some((l,i)=>l!==r[i]))return!1}return!0}function Yl(e){return e?e.aliasOf?e.aliasOf.path:e.path:""}const Jl=(e,t,n)=>e??t??n,Ff=ue({name:"RouterView",inheritAttrs:!1,props:{name:{type:String,default:"default"},route:Object},compatConfig:{MODE:3},setup(e,{attrs:t,slots:n}){const o=Se(vr),r=j(()=>e.route||o.value),l=Se(ql,0),i=j(()=>{let c=te(l);const{matched:u}=r.value;let f;for(;(f=u[c])&&!f.components;)c++;return c}),s=j(()=>r.value.matched[i.value]);qt(ql,j(()=>i.value+1)),qt(If,s),qt(vr,r);const a=be();return ut(()=>[a.value,s.value,e.name],([c,u,f],[h,m,y])=>{u&&(u.instances[f]=c,m&&m!==u&&c&&c===h&&(u.leaveGuards.size||(u.leaveGuards=m.leaveGuards),u.updateGuards.size||(u.updateGuards=m.updateGuards))),c&&u&&(!m||!mn(u,m)||!h)&&(u.enterCallbacks[f]||[]).forEach(k=>k(c))},{flush:"post"}),()=>{const c=r.value,u=e.name,f=s.value,h=f&&f.components[u];if(!h)return Ql(n.default,{Component:h,route:c});const m=f.props[u],y=m?m===!0?c.params:typeof m=="function"?m(c):m:null,x=ae(h,ve({},y,t,{onVnodeUnmounted:T=>{T.component.isUnmounted&&(f.instances[u]=null)},ref:a}));return Ql(n.default,{Component:x,route:c})||x}}});function Ql(e,t){if(!e)return null;const n=e(t);return n.length===1?n[0]:n}const Os=Ff;function Hf(e){const t=hf(e.routes,e),n=e.parseQuery||Pf,o=e.stringifyQuery||Kl,r=e.history,l=En(),i=En(),s=En(),a=Ar(pt);let c=pt;on&&e.scrollBehavior&&"scrollRestoration"in history&&(history.scrollRestoration="manual");const u=Qo.bind(null,C=>""+C),f=Qo.bind(null,Sf),h=Qo.bind(null,xo);function m(C,V){let M,G;return xs(C)?(M=t.getRecordMatcher(C),G=V):G=C,t.addRoute(G,M)}function y(C){const V=t.getRecordMatcher(C);V&&t.removeRoute(V)}function k(){return t.getRoutes().map(C=>C.record)}function x(C){return!!t.getRecordMatcher(C)}function T(C,V){if(V=ve({},V||a.value),typeof C=="string"){const g=Zo(n,C,V.path),w=t.resolve({path:g.path},V),L=r.createHref(g.fullPath);return ve(g,w,{params:h(w.params),hash:xo(g.hash),redirectedFrom:void 0,href:L})}let M;if("path"in C)M=ve({},C,{path:Zo(n,C.path,V.path).path});else{const g=ve({},C.params);for(const w in g)g[w]==null&&delete g[w];M=ve({},C,{params:f(g)}),V.params=f(V.params)}const G=t.resolve(M,V),ce=C.hash||"";G.params=u(h(G.params));const d=Bu(o,ve({},C,{hash:Lf(ce),path:G.path})),p=r.createHref(d);return ve({fullPath:d,hash:ce,query:o===Kl?Rf(C.query):C.query||{}},G,{redirectedFrom:void 0,href:p})}function A(C){return typeof C=="string"?Zo(n,C,a.value.path):ve({},C)}function v(C,V){if(c!==C)return gn(8,{from:V,to:C})}function _(C){return D(C)}function H(C){return _(ve(A(C),{replace:!0}))}function q(C){const V=C.matched[C.matched.length-1];if(V&&V.redirect){const{redirect:M}=V;let G=typeof M=="function"?M(C):M;return typeof G=="string"&&(G=G.includes("?")||G.includes("#")?G=A(G):{path:G},G.params={}),ve({query:C.query,hash:C.hash,params:"path"in G?{}:C.params},G)}}function D(C,V){const M=c=T(C),G=a.value,ce=C.state,d=C.force,p=C.replace===!0,g=q(M);if(g)return D(ve(A(g),{state:typeof g=="object"?ve({},ce,g.state):ce,force:d,replace:p}),V||M);const w=M;w.redirectedFrom=V;let L;return!d&&ju(o,G,M)&&(L=gn(16,{to:w,from:G}),Ve(G,G,!0,!1)),(L?Promise.resolve(L):P(w,G)).catch(S=>dt(S)?dt(S,2)?S:De(S):W(S,w,G)).then(S=>{if(S){if(dt(S,2))return D(ve({replace:p},A(S.to),{state:typeof S.to=="object"?ve({},ce,S.to.state):ce,force:d}),V||w)}else S=E(w,G,!0,p,ce);return U(w,G,S),S})}function b(C,V){const M=v(C,V);return M?Promise.reject(M):Promise.resolve()}function O(C){const V=Et.values().next().value;return V&&typeof V.runWithContext=="function"?V.runWithContext(C):C()}function P(C,V){let M;const[G,ce,d]=zf(C,V);M=Xo(G.reverse(),"beforeRouteLeave",C,V);for(const g of G)g.leaveGuards.forEach(w=>{M.push(Rt(w,C,V))});const p=b.bind(null,C,V);return M.push(p),Ne(M).then(()=>{M=[];for(const g of l.list())M.push(Rt(g,C,V));return M.push(p),Ne(M)}).then(()=>{M=Xo(ce,"beforeRouteUpdate",C,V);for(const g of ce)g.updateGuards.forEach(w=>{M.push(Rt(w,C,V))});return M.push(p),Ne(M)}).then(()=>{M=[];for(const g of d)if(g.beforeEnter)if(lt(g.beforeEnter))for(const w of g.beforeEnter)M.push(Rt(w,C,V));else M.push(Rt(g.beforeEnter,C,V));return M.push(p),Ne(M)}).then(()=>(C.matched.forEach(g=>g.enterCallbacks={}),M=Xo(d,"beforeRouteEnter",C,V),M.push(p),Ne(M))).then(()=>{M=[];for(const g of i.list())M.push(Rt(g,C,V));return M.push(p),Ne(M)}).catch(g=>dt(g,8)?g:Promise.reject(g))}function U(C,V,M){s.list().forEach(G=>O(()=>G(C,V,M)))}function E(C,V,M,G,ce){const d=v(C,V);if(d)return d;const p=V===pt,g=on?history.state:{};M&&(G||p?r.replace(C.fullPath,ve({scroll:p&&g&&g.scroll},ce)):r.push(C.fullPath,ce)),a.value=C,Ve(C,V,M,p),De()}let N;function ne(){N||(N=r.listen((C,V,M)=>{if(!it.listening)return;const G=T(C),ce=q(G);if(ce){D(ve(ce,{replace:!0}),G).catch(Rn);return}c=G;const d=a.value;on&&Ju(Fl(d.fullPath,M.delta),Ho()),P(G,d).catch(p=>dt(p,12)?p:dt(p,2)?(D(p.to,G).then(g=>{dt(g,20)&&!M.delta&&M.type===Fn.pop&&r.go(-1,!1)}).catch(Rn),Promise.reject()):(M.delta&&r.go(-M.delta,!1),W(p,G,d))).then(p=>{p=p||E(G,d,!1),p&&(M.delta&&!dt(p,8)?r.go(-M.delta,!1):M.type===Fn.pop&&dt(p,20)&&r.go(-1,!1)),U(G,d,p)}).catch(Rn)}))}let ie=En(),I=En(),J;function W(C,V,M){De(C);const G=I.list();return G.length?G.forEach(ce=>ce(C,V,M)):console.error(C),Promise.reject(C)}function Oe(){return J&&a.value!==pt?Promise.resolve():new Promise((C,V)=>{ie.add([C,V])})}function De(C){return J||(J=!C,ne(),ie.list().forEach(([V,M])=>C?M(C):V()),ie.reset()),C}function Ve(C,V,M,G){const{scrollBehavior:ce}=e;if(!on||!ce)return Promise.resolve();const d=!M&&Qu(Fl(C.fullPath,0))||(G||!M)&&history.state&&history.state.scroll||null;return Un().then(()=>ce(C,V,d)).then(p=>p&&Yu(p)).catch(p=>W(p,C,V))}const He=C=>r.go(C);let kt;const Et=new Set,it={currentRoute:a,listening:!0,addRoute:m,removeRoute:y,hasRoute:x,getRoutes:k,resolve:T,options:e,push:_,replace:H,go:He,back:()=>He(-1),forward:()=>He(1),beforeEach:l.add,beforeResolve:i.add,afterEach:s.add,onError:I.add,isReady:Oe,install(C){const V=this;C.component("RouterLink",$f),C.component("RouterView",Os),C.config.globalProperties.$router=V,Object.defineProperty(C.config.globalProperties,"$route",{enumerable:!0,get:()=>te(a)}),on&&!kt&&a.value===pt&&(kt=!0,_(r.location).catch(ce=>{}));const M={};for(const ce in pt)Object.defineProperty(M,ce,{get:()=>a.value[ce],enumerable:!0});C.provide(zo,V),C.provide(Vr,Pi(M)),C.provide(vr,a);const G=C.unmount;Et.add(C),C.unmount=function(){Et.delete(C),Et.size<1&&(c=pt,N&&N(),N=null,a.value=pt,kt=!1,J=!1),G()}}};function Ne(C){return C.reduce((V,M)=>V.then(()=>O(M)),Promise.resolve())}return it}function zf(e,t){const n=[],o=[],r=[],l=Math.max(t.matched.length,e.matched.length);for(let i=0;i mn(c,s))?o.push(s):n.push(s));const a=e.matched[i];a&&(t.matched.find(c=>mn(c,a))||r.push(a))}return[n,o,r]}function Zt(){return Se(zo)}function Xt(){return Se(Vr)}const Bf=({headerLinkSelector:e,headerAnchorSelector:t,delay:n,offset:o=5})=>{const r=Zt(),i=ks(()=>{var k,x;const s=Math.max(window.scrollY,document.documentElement.scrollTop,document.body.scrollTop);if(Math.abs(s-0) h.some(A=>A.hash===T.hash));for(let T=0;T =(((k=A.parentElement)==null?void 0:k.offsetTop)??0)-o,H=!v||s<(((x=v.parentElement)==null?void 0:x.offsetTop)??0)-o;if(!(_&&H))continue;const D=decodeURIComponent(r.currentRoute.value.hash),b=decodeURIComponent(A.hash);if(D===b)return;if(f){for(let O=T+1;O {window.addEventListener("scroll",i)}),qn(()=>{window.removeEventListener("scroll",i)})},Zl=async(e,t)=>{const{scrollBehavior:n}=e.options;e.options.scrollBehavior=void 0,await e.replace({query:e.currentRoute.value.query,hash:t}).finally(()=>e.options.scrollBehavior=n)},jf="a.sidebar-item",Vf=".header-anchor",Wf=300,Uf=5,Kf=wt({setup(){Bf({headerLinkSelector:jf,headerAnchorSelector:Vf,delay:Wf,offset:Uf})}}),Xl=()=>window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,qf=()=>window.scrollTo({top:0,behavior:"smooth"});const Gf=ue({name:"BackToTop",setup(){const e=be(0),t=j(()=>e.value>300),n=ks(()=>{e.value=Xl()},100);qe(()=>{e.value=Xl(),window.addEventListener("scroll",()=>n())});const o=ae("div",{class:"back-to-top",onClick:qf});return()=>ae(Gn,{name:"back-to-top"},()=>t.value?o:null)}}),Yf=wt({rootComponents:[Gf]});const Jf=ae("svg",{class:"external-link-icon",xmlns:"http://www.w3.org/2000/svg","aria-hidden":"true",focusable:"false",x:"0px",y:"0px",viewBox:"0 0 100 100",width:"15",height:"15"},[ae("path",{fill:"currentColor",d:"M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"}),ae("polygon",{fill:"currentColor",points:"45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"})]),Qf=ue({name:"ExternalLinkIcon",props:{locales:{type:Object,required:!1,default:()=>({})}},setup(e){const t=Jn(),n=j(()=>e.locales[t.value]??{openInNewWindow:"open in new window"});return()=>ae("span",[Jf,ae("span",{class:"external-link-icon-sr-only"},n.value.openInNewWindow)])}}),Zf={"/":{openInNewWindow:"open in new window"}},Xf=wt({enhance({app:e}){e.component("ExternalLinkIcon",ae(Qf,{locales:Zf}))}});/*! medium-zoom 1.0.8 | MIT License | https://github.com/francoischalifour/medium-zoom */var Bt=Object.assign||function(e){for(var t=1;t 1&&arguments[1]!==void 0?arguments[1]:{},o=window.Promise||function(E){function N(){}E(N,N)},r=function(E){var N=E.target;if(N===O){y();return}v.indexOf(N)!==-1&&k({target:N})},l=function(){if(!(H||!b.original)){var E=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0;Math.abs(q-E)>D.scrollOffset&&setTimeout(y,150)}},i=function(E){var N=E.key||E.keyCode;(N==="Escape"||N==="Esc"||N===27)&&y()},s=function(){var E=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},N=E;if(E.background&&(O.style.background=E.background),E.container&&E.container instanceof Object&&(N.container=Bt({},D.container,E.container)),E.template){var ne=ho(E.template)?E.template:document.querySelector(E.template);N.template=ne}return D=Bt({},D,N),v.forEach(function(ie){ie.dispatchEvent(nn("medium-zoom:update",{detail:{zoom:P}}))}),P},a=function(){var E=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{};return e(Bt({},D,E))},c=function(){for(var E=arguments.length,N=Array(E),ne=0;ne 0?N.reduce(function(I,J){return[].concat(I,ti(J))},[]):v;return ie.forEach(function(I){I.classList.remove("medium-zoom-image"),I.dispatchEvent(nn("medium-zoom:detach",{detail:{zoom:P}}))}),v=v.filter(function(I){return ie.indexOf(I)===-1}),P},f=function(E,N){var ne=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return v.forEach(function(ie){ie.addEventListener("medium-zoom:"+E,N,ne)}),_.push({type:"medium-zoom:"+E,listener:N,options:ne}),P},h=function(E,N){var ne=arguments.length>2&&arguments[2]!==void 0?arguments[2]:{};return v.forEach(function(ie){ie.removeEventListener("medium-zoom:"+E,N,ne)}),_=_.filter(function(ie){return!(ie.type==="medium-zoom:"+E&&ie.listener.toString()===N.toString())}),P},m=function(){var E=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},N=E.target,ne=function(){var I={width:document.documentElement.clientWidth,height:document.documentElement.clientHeight,left:0,top:0,right:0,bottom:0},J=void 0,W=void 0;if(D.container)if(D.container instanceof Object)I=Bt({},I,D.container),J=I.width-I.left-I.right-D.margin*2,W=I.height-I.top-I.bottom-D.margin*2;else{var Oe=ho(D.container)?D.container:document.querySelector(D.container),De=Oe.getBoundingClientRect(),Ve=De.width,He=De.height,kt=De.left,Et=De.top;I=Bt({},I,{width:Ve,height:He,left:kt,top:Et})}J=J||I.width-D.margin*2,W=W||I.height-D.margin*2;var it=b.zoomedHd||b.original,Ne=ei(it)?J:it.naturalWidth||J,C=ei(it)?W:it.naturalHeight||W,V=it.getBoundingClientRect(),M=V.top,G=V.left,ce=V.width,d=V.height,p=Math.min(Math.max(ce,Ne),J)/ce,g=Math.min(Math.max(d,C),W)/d,w=Math.min(p,g),L=(-G+(J-ce)/2+D.margin+I.left)/w,S=(-M+(W-d)/2+D.margin+I.top)/w,z="scale("+w+") translate3d("+L+"px, "+S+"px, 0)";b.zoomed.style.transform=z,b.zoomedHd&&(b.zoomedHd.style.transform=z)};return new o(function(ie){if(N&&v.indexOf(N)===-1){ie(P);return}var I=function Ve(){H=!1,b.zoomed.removeEventListener("transitionend",Ve),b.original.dispatchEvent(nn("medium-zoom:opened",{detail:{zoom:P}})),ie(P)};if(b.zoomed){ie(P);return}if(N)b.original=N;else if(v.length>0){var J=v;b.original=J[0]}else{ie(P);return}if(b.original.dispatchEvent(nn("medium-zoom:open",{detail:{zoom:P}})),q=window.pageYOffset||document.documentElement.scrollTop||document.body.scrollTop||0,H=!0,b.zoomed=nd(b.original),document.body.appendChild(O),D.template){var W=ho(D.template)?D.template:document.querySelector(D.template);b.template=document.createElement("div"),b.template.appendChild(W.content.cloneNode(!0)),document.body.appendChild(b.template)}if(b.original.parentElement&&b.original.parentElement.tagName==="PICTURE"&&b.original.currentSrc&&(b.zoomed.src=b.original.currentSrc),document.body.appendChild(b.zoomed),window.requestAnimationFrame(function(){document.body.classList.add("medium-zoom--opened")}),b.original.classList.add("medium-zoom-image--hidden"),b.zoomed.classList.add("medium-zoom-image--opened"),b.zoomed.addEventListener("click",y),b.zoomed.addEventListener("transitionend",I),b.original.getAttribute("data-zoom-src")){b.zoomedHd=b.zoomed.cloneNode(),b.zoomedHd.removeAttribute("srcset"),b.zoomedHd.removeAttribute("sizes"),b.zoomedHd.removeAttribute("loading"),b.zoomedHd.src=b.zoomed.getAttribute("data-zoom-src"),b.zoomedHd.onerror=function(){clearInterval(Oe),console.warn("Unable to reach the zoom image target "+b.zoomedHd.src),b.zoomedHd=null,ne()};var Oe=setInterval(function(){b.zoomedHd.complete&&(clearInterval(Oe),b.zoomedHd.classList.add("medium-zoom-image--opened"),b.zoomedHd.addEventListener("click",y),document.body.appendChild(b.zoomedHd),ne())},10)}else if(b.original.hasAttribute("srcset")){b.zoomedHd=b.zoomed.cloneNode(),b.zoomedHd.removeAttribute("sizes"),b.zoomedHd.removeAttribute("loading");var De=b.zoomedHd.addEventListener("load",function(){b.zoomedHd.removeEventListener("load",De),b.zoomedHd.classList.add("medium-zoom-image--opened"),b.zoomedHd.addEventListener("click",y),document.body.appendChild(b.zoomedHd),ne()})}else ne()})},y=function(){return new o(function(E){if(H||!b.original){E(P);return}var N=function ne(){b.original.classList.remove("medium-zoom-image--hidden"),document.body.removeChild(b.zoomed),b.zoomedHd&&document.body.removeChild(b.zoomedHd),document.body.removeChild(O),b.zoomed.classList.remove("medium-zoom-image--opened"),b.template&&document.body.removeChild(b.template),H=!1,b.zoomed.removeEventListener("transitionend",ne),b.original.dispatchEvent(nn("medium-zoom:closed",{detail:{zoom:P}})),b.original=null,b.zoomed=null,b.zoomedHd=null,b.template=null,E(P)};H=!0,document.body.classList.remove("medium-zoom--opened"),b.zoomed.style.transform="",b.zoomedHd&&(b.zoomedHd.style.transform=""),b.template&&(b.template.style.transition="opacity 150ms",b.template.style.opacity=0),b.original.dispatchEvent(nn("medium-zoom:close",{detail:{zoom:P}})),b.zoomed.addEventListener("transitionend",N)})},k=function(){var E=arguments.length>0&&arguments[0]!==void 0?arguments[0]:{},N=E.target;return b.original?y():m({target:N})},x=function(){return D},T=function(){return v},A=function(){return b.original},v=[],_=[],H=!1,q=0,D=n,b={original:null,zoomed:null,zoomedHd:null,template:null};Object.prototype.toString.call(t)==="[object Object]"?D=t:(t||typeof t=="string")&&c(t),D=Bt({margin:0,background:"#fff",scrollOffset:40,container:null,template:null},D);var O=td(D.background);document.addEventListener("click",r),document.addEventListener("keyup",i),document.addEventListener("scroll",l),window.addEventListener("resize",y);var P={open:m,close:y,toggle:k,update:s,clone:a,attach:c,detach:u,on:f,off:h,getOptions:x,getImages:T,getZoomedImage:A};return P};function rd(e,t){t===void 0&&(t={});var n=t.insertAt;if(!(!e||typeof document>"u")){var o=document.head||document.getElementsByTagName("head")[0],r=document.createElement("style");r.type="text/css",n==="top"&&o.firstChild?o.insertBefore(r,o.firstChild):o.appendChild(r),r.styleSheet?r.styleSheet.cssText=e:r.appendChild(document.createTextNode(e))}}var ld=".medium-zoom-overlay{position:fixed;top:0;right:0;bottom:0;left:0;opacity:0;transition:opacity .3s;will-change:opacity}.medium-zoom--opened .medium-zoom-overlay{cursor:pointer;cursor:zoom-out;opacity:1}.medium-zoom-image{cursor:pointer;cursor:zoom-in;transition:transform .3s cubic-bezier(.2,0,.2,1)!important}.medium-zoom-image--hidden{visibility:hidden}.medium-zoom-image--opened{position:relative;cursor:pointer;cursor:zoom-out;will-change:transform}";rd(ld);const id=od,sd=Symbol("mediumZoom");const ad=".theme-default-content > img, .theme-default-content :not(a) > img",cd={},ud=300,fd=wt({enhance({app:e,router:t}){const n=id(cd);n.refresh=(o=ad)=>{n.detach(),n.attach(o)},e.provide(sd,n),t.afterEach(()=>{setTimeout(()=>n.refresh(),ud)})}});/** + * NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT + */const fe={settings:{minimum:.08,easing:"ease",speed:200,trickle:!0,trickleRate:.02,trickleSpeed:800,barSelector:'[role="bar"]',parent:"body",template:''},status:null,set:e=>{const t=fe.isStarted();e=er(e,fe.settings.minimum,1),fe.status=e===1?null:e;const n=fe.render(!t),o=n.querySelector(fe.settings.barSelector),r=fe.settings.speed,l=fe.settings.easing;return n.offsetWidth,dd(i=>{so(o,{transform:"translate3d("+ni(e)+"%,0,0)",transition:"all "+r+"ms "+l}),e===1?(so(n,{transition:"none",opacity:"1"}),n.offsetWidth,setTimeout(function(){so(n,{transition:"all "+r+"ms linear",opacity:"0"}),setTimeout(function(){fe.remove(),i()},r)},r)):setTimeout(()=>i(),r)}),fe},isStarted:()=>typeof fe.status=="number",start:()=>{fe.status||fe.set(0);const e=()=>{setTimeout(()=>{fe.status&&(fe.trickle(),e())},fe.settings.trickleSpeed)};return fe.settings.trickle&&e(),fe},done:e=>!e&&!fe.status?fe:fe.inc(.3+.5*Math.random()).set(1),inc:e=>{let t=fe.status;return t?(typeof e!="number"&&(e=(1-t)*er(Math.random()*t,.1,.95)),t=er(t+e,0,.994),fe.set(t)):fe.start()},trickle:()=>fe.inc(Math.random()*fe.settings.trickleRate),render:e=>{if(fe.isRendered())return document.getElementById("nprogress");oi(document.documentElement,"nprogress-busy");const t=document.createElement("div");t.id="nprogress",t.innerHTML=fe.settings.template;const n=t.querySelector(fe.settings.barSelector),o=e?"-100":ni(fe.status||0),r=document.querySelector(fe.settings.parent);return so(n,{transition:"all 0 linear",transform:"translate3d("+o+"%,0,0)"}),r!==document.body&&oi(r,"nprogress-custom-parent"),r==null||r.appendChild(t),t},remove:()=>{ri(document.documentElement,"nprogress-busy"),ri(document.querySelector(fe.settings.parent),"nprogress-custom-parent");const e=document.getElementById("nprogress");e&&hd(e)},isRendered:()=>!!document.getElementById("nprogress")},er=(e,t,n)=>e n?n:e,ni=e=>(-1+e)*100,dd=function(){const e=[];function t(){const n=e.shift();n&&n(t)}return function(n){e.push(n),e.length===1&&t()}}(),so=function(){const e=["Webkit","O","Moz","ms"],t={};function n(i){return i.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(s,a){return a.toUpperCase()})}function o(i){const s=document.body.style;if(i in s)return i;let a=e.length;const c=i.charAt(0).toUpperCase()+i.slice(1);let u;for(;a--;)if(u=e[a]+c,u in s)return u;return i}function r(i){return i=n(i),t[i]??(t[i]=o(i))}function l(i,s,a){s=r(s),i.style[s]=a}return function(i,s){for(const a in s){const c=s[a];c!==void 0&&Object.prototype.hasOwnProperty.call(s,a)&&l(i,a,c)}}}(),Ns=(e,t)=>(typeof e=="string"?e:Wr(e)).indexOf(" "+t+" ")>=0,oi=(e,t)=>{const n=Wr(e),o=n+t;Ns(n,t)||(e.className=o.substring(1))},ri=(e,t)=>{const n=Wr(e);if(!Ns(e,t))return;const o=n.replace(" "+t+" "," ");e.className=o.substring(1,o.length-1)},Wr=e=>(" "+(e.className||"")+" ").replace(/\s+/gi," "),hd=e=>{e&&e.parentNode&&e.parentNode.removeChild(e)};const pd=()=>{qe(()=>{const e=Zt(),t=new Set;t.add(e.currentRoute.value.path),e.beforeEach(n=>{t.has(n.path)||fe.start()}),e.afterEach(n=>{t.add(n.path),fe.done()})})},md=wt({setup(){pd()}}),gd=JSON.parse(`{"repo":"Apillon/wiki","docsDir":"","colorModeSwitch":false,"colorMode":"dark","locales":{"/":{"sidebar":{"/":[],"/about":[{"text":"About","collapsible":true,"children":["/about/1-navigation.md","/about/2-what-is-apillon.md","/about/3-why-apillon.md","/about/4-how-does-apillon-work.md","/about/5-developing-web3-with-apillon.md","/about/6-web3-up-close.md","/about/7-community.md","/about/8-nctr-token.md"]},{"text":"Web3-services","collapsible":true,"children":["/web3-services/1-good-to-know.md","/web3-services/2-web3-storage.md","/web3-services/3-web3-hosting.md","/web3-services/4-nfts.md","/web3-services/5-web3-authentication.md","/web3-services/6-web3-social.md","/web3-services/7-web3-compute.md"]},{"text":"Build","collapsible":true,"children":["/build/1-apillon-api.md","/build/2-storage-api.md","/build/3-hosting-api.md","/build/4-nfts-api.md","/build/5-apillon-sdk.md","/build/6-apillon-cli.md","/build/7-apillon-oauth-integration.md","/build/8-computing-api.md","/build/9-social-api.md","/build/10-flutter-sdk.md"]}],"/web3-services":[{"text":"About","collapsible":true,"children":["/about/1-navigation.md","/about/2-what-is-apillon.md","/about/3-why-apillon.md","/about/4-how-does-apillon-work.md","/about/5-developing-web3-with-apillon.md","/about/6-web3-up-close.md","/about/7-community.md","/about/8-nctr-token.md"]},{"text":"Web3-services","collapsible":true,"children":["/web3-services/1-good-to-know.md","/web3-services/2-web3-storage.md","/web3-services/3-web3-hosting.md","/web3-services/4-nfts.md","/web3-services/5-web3-authentication.md","/web3-services/6-web3-social.md","/web3-services/7-web3-compute.md"]},{"text":"Build","collapsible":true,"children":["/build/1-apillon-api.md","/build/2-storage-api.md","/build/3-hosting-api.md","/build/4-nfts-api.md","/build/5-apillon-sdk.md","/build/6-apillon-cli.md","/build/7-apillon-oauth-integration.md","/build/8-computing-api.md","/build/9-social-api.md","/build/10-flutter-sdk.md"]}],"/build":[{"text":"About","collapsible":true,"children":["/about/1-navigation.md","/about/2-what-is-apillon.md","/about/3-why-apillon.md","/about/4-how-does-apillon-work.md","/about/5-developing-web3-with-apillon.md","/about/6-web3-up-close.md","/about/7-community.md","/about/8-nctr-token.md"]},{"text":"Web3-services","collapsible":true,"children":["/web3-services/1-good-to-know.md","/web3-services/2-web3-storage.md","/web3-services/3-web3-hosting.md","/web3-services/4-nfts.md","/web3-services/5-web3-authentication.md","/web3-services/6-web3-social.md","/web3-services/7-web3-compute.md"]},{"text":"Build","collapsible":true,"children":["/build/1-apillon-api.md","/build/2-storage-api.md","/build/3-hosting-api.md","/build/4-nfts-api.md","/build/5-apillon-sdk.md","/build/6-apillon-cli.md","/build/7-apillon-oauth-integration.md","/build/8-computing-api.md","/build/9-social-api.md","/build/10-flutter-sdk.md"]}]},"contributors":false,"selectLanguageName":"English"}},"logo":"assets/logo.svg","logoDark":"assets/logo-dark.svg","navbar":[{"text":"About","link":"/about/","children":["/about/1-navigation.md","/about/2-what-is-apillon.md","/about/3-why-apillon.md","/about/4-how-does-apillon-work.md","/about/5-developing-web3-with-apillon.md","/about/6-web3-up-close.md","/about/7-community.md","/about/8-nctr-token.md"]},{"text":"Web3-services","link":"/web3-services/","children":["/web3-services/1-good-to-know.md","/web3-services/2-web3-storage.md","/web3-services/3-web3-hosting.md","/web3-services/4-nfts.md","/web3-services/5-web3-authentication.md","/web3-services/6-web3-social.md","/web3-services/7-web3-compute.md"]},{"text":"Build","link":"/build/","children":["/build/1-apillon-api.md","/build/2-storage-api.md","/build/3-hosting-api.md","/build/4-nfts-api.md","/build/5-apillon-sdk.md","/build/6-apillon-cli.md","/build/7-apillon-oauth-integration.md","/build/8-computing-api.md","/build/9-social-api.md","/build/10-flutter-sdk.md"]}],"selectLanguageText":"Languages","selectLanguageAriaLabel":"Select language","sidebar":"auto","sidebarDepth":2,"editLink":true,"editLinkText":"Edit this page","lastUpdated":true,"lastUpdatedText":"Last Updated","contributors":true,"contributorsText":"Contributors","notFound":["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."],"backToHome":"Take me home","openInNewWindow":"open in new window","toggleColorMode":"toggle color mode","toggleSidebar":"toggle sidebar"}`),vd=be(gd),$s=()=>vd,Ds=Symbol(""),bd=()=>{const e=Se(Ds);if(!e)throw new Error("useThemeLocaleData() is called without provider.");return e},_d=(e,t)=>{const{locales:n,...o}=e;return{...o,...n==null?void 0:n[t]}},yd=wt({enhance({app:e}){const t=$s(),n=e._context.provides[Fr],o=j(()=>_d(t.value,n.value));e.provide(Ds,o),Object.defineProperties(e.config.globalProperties,{$theme:{get(){return t.value}},$themeLocale:{get(){return o.value}}})}}),wd=ue({__name:"Badge",props:{type:{type:String,required:!1,default:"tip"},text:{type:String,required:!1,default:""},vertical:{type:String,required:!1,default:void 0}},setup(e){return(t,n)=>(B(),ee("span",{class:Ue(["badge",e.type]),style:Bn({verticalAlign:e.vertical})},[we(t.$slots,"default",{},()=>[Mt(Re(e.text),1)])],6))}}),xe=(e,t)=>{const n=e.__vccOpts||e;for(const[o,r]of t)n[o]=r;return n},kd=xe(wd,[["__file","Badge.vue"]]),Ed=ue({name:"CodeGroup",slots:Object,setup(e,{slots:t}){const n=be(-1),o=be([]),r=(s=n.value)=>{s {s>0?n.value=s-1:n.value=o.value.length-1,o.value[n.value].focus()},i=(s,a)=>{s.key===" "||s.key==="Enter"?(s.preventDefault(),n.value=a):s.key==="ArrowRight"?(s.preventDefault(),r(a)):s.key==="ArrowLeft"&&(s.preventDefault(),l(a))};return()=>{var a;const s=(((a=t.default)==null?void 0:a.call(t))||[]).filter(c=>c.type.name==="CodeGroupItem").map(c=>(c.props===null&&(c.props={}),c));return s.length===0?null:(n.value<0||n.value>s.length-1?(n.value=s.findIndex(c=>c.props.active===""||c.props.active===!0),n.value===-1&&(n.value=0)):s.forEach((c,u)=>{c.props.active=u===n.value}),ae("div",{class:"code-group"},[ae("div",{class:"code-group__nav"},ae("ul",{class:"code-group__ul"},s.map((c,u)=>{const f=u===n.value;return ae("li",{class:"code-group__li"},ae("button",{ref:h=>{h&&(o.value[u]=h)},class:{"code-group__nav-tab":!0,"code-group__nav-tab-active":f},ariaPressed:f,ariaExpanded:f,onClick:()=>n.value=u,onKeydown:h=>i(h,u)},c.props.title))}))),s]))}}}),Cd=["aria-selected"],xd=ue({name:"CodeGroupItem"}),Ld=ue({...xd,props:{title:{type:String,required:!0},active:{type:Boolean,required:!1,default:!1}},setup(e){return(t,n)=>(B(),ee("div",{class:Ue(["code-group-item",{"code-group-item__active":e.active}]),"aria-selected":e.active},[we(t.$slots,"default")],10,Cd))}}),Td=xe(Ld,[["__file","CodeGroupItem.vue"]]);function li(e,t){var n;const o=Ar();return Wi(()=>{o.value=e()},{...t,flush:(n=t==null?void 0:t.flush)!=null?n:"sync"}),Vn(o)}function Ms(e){return _i()?(ha(e),!0):!1}function vn(e){return typeof e=="function"?e():te(e)}const Ad=typeof window<"u"&&typeof document<"u";typeof WorkerGlobalScope<"u"&&globalThis instanceof WorkerGlobalScope;const Sd=Object.prototype.toString,Pd=e=>Sd.call(e)==="[object Object]",Rd=()=>{};function Id(e,t){function n(...o){return new Promise((r,l)=>{Promise.resolve(e(()=>t.apply(this,o),{fn:t,thisArg:this,args:o})).then(r).catch(l)})}return n}const Fs=e=>e();function Od(e=Fs){const t=be(!0);function n(){t.value=!1}function o(){t.value=!0}const r=(...l)=>{t.value&&e(...l)};return{isActive:Vn(t),pause:n,resume:o,eventFilter:r}}function Nd(e,t,n={}){const{eventFilter:o=Fs,...r}=n;return ut(e,Id(o,t),r)}function $d(e,t,n={}){const{eventFilter:o,...r}=n,{eventFilter:l,pause:i,resume:s,isActive:a}=Od(o);return{stop:Nd(e,t,{...r,eventFilter:l}),pause:i,resume:s,isActive:a}}function Dd(e,t=!0){Nr()?qe(e):t?e():Un(e)}function Md(e=!1,t={}){const{truthyValue:n=!0,falsyValue:o=!1}=t,r=$e(e),l=be(e);function i(s){if(arguments.length)return l.value=s,l.value;{const a=vn(n);return l.value=l.value===a?vn(o):a,l.value}}return r?i:[l,i]}function Fd(e){var t;const n=vn(e);return(t=n==null?void 0:n.$el)!=null?t:n}const Lo=Ad?window:void 0;function ii(...e){let t,n,o,r;if(typeof e[0]=="string"||Array.isArray(e[0])?([n,o,r]=e,t=Lo):[t,n,o,r]=e,!t)return Rd;Array.isArray(n)||(n=[n]),Array.isArray(o)||(o=[o]);const l=[],i=()=>{l.forEach(u=>u()),l.length=0},s=(u,f,h,m)=>(u.addEventListener(f,h,m),()=>u.removeEventListener(f,h,m)),a=ut(()=>[Fd(t),vn(r)],([u,f])=>{if(i(),!u)return;const h=Pd(f)?{...f}:f;l.push(...n.flatMap(m=>o.map(y=>s(u,m,y,h))))},{immediate:!0,flush:"post"}),c=()=>{a(),i()};return Ms(c),c}function Hd(){const e=be(!1);return Nr()&&qe(()=>{e.value=!0}),e}function zd(e){const t=Hd();return j(()=>(t.value,!!e()))}function Bd(e,t={}){const{window:n=Lo}=t,o=zd(()=>n&&"matchMedia"in n&&typeof n.matchMedia=="function");let r;const l=be(!1),i=c=>{l.value=c.matches},s=()=>{r&&("removeEventListener"in r?r.removeEventListener("change",i):r.removeListener(i))},a=Wi(()=>{o.value&&(s(),r=n.matchMedia(vn(e)),"addEventListener"in r?r.addEventListener("change",i):r.addListener(i),l.value=r.matches)});return Ms(()=>{a(),s(),r=void 0}),l}const ao=typeof globalThis<"u"?globalThis:typeof window<"u"?window:typeof global<"u"?global:typeof self<"u"?self:{},co="__vueuse_ssr_handlers__",jd=Vd();function Vd(){return co in ao||(ao[co]=ao[co]||{}),ao[co]}function Wd(e,t){return jd[e]||t}function Ud(e){return e==null?"any":e instanceof Set?"set":e instanceof Map?"map":e instanceof Date?"date":typeof e=="boolean"?"boolean":typeof e=="string"?"string":typeof e=="object"?"object":Number.isNaN(e)?"any":"number"}const Kd={boolean:{read:e=>e==="true",write:e=>String(e)},object:{read:e=>JSON.parse(e),write:e=>JSON.stringify(e)},number:{read:e=>Number.parseFloat(e),write:e=>String(e)},any:{read:e=>e,write:e=>String(e)},string:{read:e=>e,write:e=>String(e)},map:{read:e=>new Map(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e.entries()))},set:{read:e=>new Set(JSON.parse(e)),write:e=>JSON.stringify(Array.from(e))},date:{read:e=>new Date(e),write:e=>e.toISOString()}},si="vueuse-storage";function qd(e,t,n,o={}){var r;const{flush:l="pre",deep:i=!0,listenToStorageChanges:s=!0,writeDefaults:a=!0,mergeDefaults:c=!1,shallow:u,window:f=Lo,eventFilter:h,onError:m=O=>{console.error(O)},initOnMounted:y}=o,k=(u?Ar:be)(typeof t=="function"?t():t);if(!n)try{n=Wd("getDefaultStorage",()=>{var O;return(O=Lo)==null?void 0:O.localStorage})()}catch(O){m(O)}if(!n)return k;const x=vn(t),T=Ud(x),A=(r=o.serializer)!=null?r:Kd[T],{pause:v,resume:_}=$d(k,()=>H(k.value),{flush:l,deep:i,eventFilter:h});return f&&s&&Dd(()=>{ii(f,"storage",b),ii(f,si,D),y&&b()}),y||b(),k;function H(O){try{if(O==null)n.removeItem(e);else{const P=A.write(O),U=n.getItem(e);U!==P&&(n.setItem(e,P),f&&f.dispatchEvent(new CustomEvent(si,{detail:{key:e,oldValue:U,newValue:P,storageArea:n}})))}}catch(P){m(P)}}function q(O){const P=O?O.newValue:n.getItem(e);if(P==null)return a&&x!==null&&n.setItem(e,A.write(x)),x;if(!O&&c){const U=A.read(P);return typeof c=="function"?c(U,x):T==="object"&&!Array.isArray(U)?{...x,...U}:U}else return typeof P!="string"?P:A.read(P)}function D(O){b(O.detail)}function b(O){if(!(O&&O.storageArea!==n)){if(O&&O.key==null){k.value=x;return}if(!(O&&O.key!==e)){v();try{(O==null?void 0:O.newValue)!==A.write(k.value)&&(k.value=q(O))}catch(P){m(P)}finally{O?Un(_):_()}}}}}function Gd(e){return Bd("(prefers-color-scheme: dark)",e)}const Yd=()=>$s(),je=()=>bd(),Hs=Symbol(""),Ur=()=>{const e=Se(Hs);if(!e)throw new Error("useDarkMode() is called without provider.");return e},Jd=()=>{const e=je(),t=Gd(),n=qd("vuepress-color-scheme",e.value.colorMode),o=j({get(){return e.value.colorModeSwitch?n.value==="auto"?t.value:n.value==="dark":e.value.colorMode==="dark"},set(r){r===t.value?n.value="auto":n.value=r?"dark":"light"}});qt(Hs,o),Qd(o)},Qd=e=>{const t=(n=e.value)=>{const o=window==null?void 0:window.document.querySelector("html");o==null||o.classList.toggle("dark",n)};qe(()=>{ut(e,t,{immediate:!0})}),Do(()=>t())},zs=(...e)=>{const n=Zt().resolve(...e),o=n.matched[n.matched.length-1];if(!(o!=null&&o.redirect))return n;const{redirect:r}=o,l=le(r)?r(n):r,i=me(l)?{path:l}:l;return zs({hash:n.hash,query:n.query,params:n.params,...i})},Kr=e=>{const t=zs(encodeURI(e));return{text:t.meta.title||e,link:t.name==="404"?e:t.fullPath}};let tr=null,Cn=null;const Zd={wait:()=>tr,pending:()=>{tr=new Promise(e=>Cn=e)},resolve:()=>{Cn==null||Cn(),tr=null,Cn=null}},Bs=()=>Zd,js=Symbol("sidebarItems"),qr=()=>{const e=Se(js);if(!e)throw new Error("useSidebarItems() is called without provider.");return e},Xd=()=>{const e=je(),t=vt(),n=j(()=>eh(t.value,e.value));qt(js,n)},eh=(e,t)=>{const n=e.sidebar??t.sidebar??"auto",o=e.sidebarDepth??t.sidebarDepth??2;return e.home||n===!1?[]:n==="auto"?nh(o):X(n)?Vs(n,o):Mr(n)?oh(n,o):[]},th=(e,t)=>({text:e.title,link:e.link,children:Gr(e.children,t)}),Gr=(e,t)=>t>0?e.map(n=>th(n,t-1)):[],nh=e=>{const t=Yt();return[{text:t.value.title,children:Gr(t.value.headers,e)}]},Vs=(e,t)=>{const n=Xt(),o=Yt(),r=l=>{var s;let i;if(me(l)?i=Kr(l):i=l,i.children)return{...i,children:i.children.map(a=>r(a))};if(i.link===n.path){const a=((s=o.value.headers[0])==null?void 0:s.level)===1?o.value.headers[0].children:o.value.headers;return{...i,children:Gr(a,t)}}return i};return e.map(l=>r(l))},oh=(e,t)=>{const n=Xt(),o=hs(e,n.path),r=e[o]??[];return Vs(r,t)},rh="719px",lh={mobile:rh};var Hn;(function(e){e.MOBILE="mobile"})(Hn||(Hn={}));var di;const ih={[Hn.MOBILE]:Number.parseInt((di=lh.mobile)==null?void 0:di.replace("px",""),10)},Ws=(e,t)=>{const n=ih[e];Number.isInteger(n)&&qe(()=>{t(n),window.addEventListener("resize",()=>t(n),!1),window.addEventListener("orientationchange",()=>t(n),!1)})},sh={},ah={class:"theme-default-content"};function ch(e,t){const n=_t("Content");return B(),ee("div",ah,[oe(n)])}const uh=xe(sh,[["render",ch],["__file","HomeContent.vue"]]),fh={key:0,class:"features"},dh=ue({__name:"HomeFeatures",setup(e){const t=vt(),n=j(()=>X(t.value.features)?t.value.features:[]);return(o,r)=>n.value.length?(B(),ee("div",fh,[(B(!0),ee(ke,null,$t(n.value,l=>(B(),ee("div",{key:l.title,class:"feature"},[he("h2",null,Re(l.title),1),he("p",null,Re(l.details),1)]))),128))])):Le("v-if",!0)}}),hh=xe(dh,[["__file","HomeFeatures.vue"]]),ph=["innerHTML"],mh=["textContent"],gh=ue({__name:"HomeFooter",setup(e){const t=vt(),n=j(()=>t.value.footer),o=j(()=>t.value.footerHtml);return(r,l)=>n.value?(B(),ee(ke,{key:0},[Le(" eslint-disable-next-line vue/no-v-html "),o.value?(B(),ee("div",{key:0,class:"footer",innerHTML:n.value},null,8,ph)):(B(),ee("div",{key:1,class:"footer",textContent:Re(n.value)},null,8,mh))],64)):Le("v-if",!0)}}),vh=xe(gh,[["__file","HomeFooter.vue"]]),bh=["href","rel","target","aria-label"],_h=ue({inheritAttrs:!1}),yh=ue({..._h,__name:"AutoLink",props:{item:{type:Object,required:!0}},setup(e){const t=e,n=Xt(),o=ys(),{item:r}=Io(t),l=j(()=>Yn(r.value.link)),i=j(()=>Lu(r.value.link)||Tu(r.value.link)),s=j(()=>{if(!i.value){if(r.value.target)return r.value.target;if(l.value)return"_blank"}}),a=j(()=>s.value==="_blank"),c=j(()=>!l.value&&!i.value&&!a.value),u=j(()=>{if(!i.value){if(r.value.rel)return r.value.rel;if(a.value)return"noopener noreferrer"}}),f=j(()=>r.value.ariaLabel||r.value.text),h=j(()=>{const k=Object.keys(o.value.locales);return k.length?!k.some(x=>x===r.value.link):r.value.link!=="/"}),m=j(()=>h.value?n.path.startsWith(r.value.link):!1),y=j(()=>c.value?r.value.activeMatch?new RegExp(r.value.activeMatch).test(n.path):m.value:!1);return(k,x)=>{const T=_t("RouterLink"),A=_t("AutoLinkExternalIcon");return c.value?(B(),Ae(T,hr({key:0,class:{"router-link-active":y.value},to:te(r).link,"aria-label":f.value},k.$attrs),{default:Me(()=>[we(k.$slots,"before"),Mt(" "+Re(te(r).text)+" ",1),we(k.$slots,"after")]),_:3},16,["class","to","aria-label"])):(B(),ee("a",hr({key:1,class:"external-link",href:te(r).link,rel:u.value,target:s.value,"aria-label":f.value},k.$attrs),[we(k.$slots,"before"),Mt(" "+Re(te(r).text)+" ",1),a.value?(B(),Ae(A,{key:0})):Le("v-if",!0),we(k.$slots,"after")],16,bh))}}}),bt=xe(yh,[["__file","AutoLink.vue"]]),wh={class:"hero"},kh={key:0,id:"main-title"},Eh={key:1,class:"description"},Ch={key:2,class:"actions"},xh=ue({__name:"HomeHero",setup(e){const t=vt(),n=Hr(),o=Ur(),r=j(()=>o.value&&t.value.heroImageDark!==void 0?t.value.heroImageDark:t.value.heroImage),l=j(()=>t.value.heroAlt||s.value||"hero"),i=j(()=>t.value.heroHeight||280),s=j(()=>t.value.heroText===null?null:t.value.heroText||n.value.title||"Hello"),a=j(()=>t.value.tagline===null?null:t.value.tagline||n.value.description||"Welcome to your VuePress site"),c=j(()=>X(t.value.actions)?t.value.actions.map(({text:f,link:h,type:m="primary"})=>({text:f,link:h,type:m})):[]),u=()=>{if(!r.value)return null;const f=ae("img",{src:Br(r.value),alt:l.value,height:i.value});return t.value.heroImageDark===void 0?f:ae(zr,()=>f)};return(f,h)=>(B(),ee("header",wh,[oe(u),s.value?(B(),ee("h1",kh,Re(s.value),1)):Le("v-if",!0),a.value?(B(),ee("p",Eh,Re(a.value),1)):Le("v-if",!0),c.value.length?(B(),ee("p",Ch,[(B(!0),ee(ke,null,$t(c.value,m=>(B(),Ae(bt,{key:m.text,class:Ue(["action-button",[m.type]]),item:m},null,8,["class","item"]))),128))])):Le("v-if",!0)]))}}),Lh=xe(xh,[["__file","HomeHero.vue"]]),Th={class:"home"},Ah=ue({__name:"Home",setup(e){return(t,n)=>(B(),ee("main",Th,[oe(Lh),oe(hh),oe(uh),oe(vh)]))}}),Sh=xe(Ah,[["__file","Home.vue"]]),Ph=ue({__name:"NavbarBrand",setup(e){const t=Jn(),n=Hr(),o=je(),r=Ur(),l=j(()=>o.value.home||t.value),i=j(()=>n.value.title),s=j(()=>r.value&&o.value.logoDark!==void 0?o.value.logoDark:o.value.logo),a=()=>{if(!s.value)return null;const c=ae("img",{class:"logo",src:Br(s.value),alt:i.value});return o.value.logoDark===void 0?c:ae(zr,()=>c)};return(c,u)=>{const f=_t("RouterLink");return B(),Ae(f,{to:l.value},{default:Me(()=>[oe(a),i.value?(B(),ee("span",{key:0,class:Ue(["site-name",{"can-hide":s.value}])},Re(i.value),3)):Le("v-if",!0)]),_:1},8,["to"])}}}),Rh=xe(Ph,[["__file","NavbarBrand.vue"]]),Ih=ue({__name:"DropdownTransition",setup(e){const t=o=>{o.style.height=o.scrollHeight+"px"},n=o=>{o.style.height=""};return(o,r)=>(B(),Ae(Gn,{name:"dropdown",onEnter:t,onAfterEnter:n,onBeforeLeave:t},{default:Me(()=>[we(o.$slots,"default")]),_:3}))}}),Us=xe(Ih,[["__file","DropdownTransition.vue"]]),Oh=["aria-label"],Nh={class:"title"},$h=he("span",{class:"arrow down"},null,-1),Dh=["aria-label"],Mh={class:"title"},Fh={class:"navbar-dropdown"},Hh={class:"navbar-dropdown-subtitle"},zh={key:1},Bh={class:"navbar-dropdown-subitem-wrapper"},jh=ue({__name:"NavbarDropdown",props:{item:{type:Object,required:!0}},setup(e){const t=e,{item:n}=Io(t),o=j(()=>n.value.ariaLabel||n.value.text),r=be(!1),l=Xt();ut(()=>l.path,()=>{r.value=!1});const i=a=>{a.detail===0?r.value=!r.value:r.value=!1},s=(a,c)=>c[c.length-1]===a;return(a,c)=>(B(),ee("div",{class:Ue(["navbar-dropdown-wrapper",{open:r.value}])},[he("button",{class:"navbar-dropdown-title",type:"button","aria-label":o.value,onClick:i},[he("span",Nh,Re(te(n).text),1),$h],8,Oh),he("button",{class:"navbar-dropdown-title-mobile",type:"button","aria-label":o.value,onClick:c[0]||(c[0]=u=>r.value=!r.value)},[he("span",Mh,Re(te(n).text),1),he("span",{class:Ue(["arrow",r.value?"down":"right"])},null,2)],8,Dh),oe(Us,null,{default:Me(()=>[_o(he("ul",Fh,[(B(!0),ee(ke,null,$t(te(n).children,u=>(B(),ee("li",{key:u.text,class:"navbar-dropdown-item"},[u.children?(B(),ee(ke,{key:0},[he("h4",Hh,[u.link?(B(),Ae(bt,{key:0,item:u,onFocusout:f=>s(u,te(n).children)&&u.children.length===0&&(r.value=!1)},null,8,["item","onFocusout"])):(B(),ee("span",zh,Re(u.text),1))]),he("ul",Bh,[(B(!0),ee(ke,null,$t(u.children,f=>(B(),ee("li",{key:f.link,class:"navbar-dropdown-subitem"},[oe(bt,{item:f,onFocusout:h=>s(f,u.children)&&s(u,te(n).children)&&(r.value=!1)},null,8,["item","onFocusout"])]))),128))])],64)):(B(),Ae(bt,{key:1,item:u,onFocusout:f=>s(u,te(n).children)&&(r.value=!1)},null,8,["item","onFocusout"]))]))),128))],512),[[Co,r.value]])]),_:1})],2))}}),Vh=xe(jh,[["__file","NavbarDropdown.vue"]]),ai=e=>decodeURI(e).replace(/#.*$/,"").replace(/(index)?\.(md|html)$/,""),Wh=(e,t)=>{if(t.hash===e)return!0;const n=ai(t.path),o=ai(e);return n===o},Ks=(e,t)=>e.link&&Wh(e.link,t)?!0:e.children?e.children.some(n=>Ks(n,t)):!1,qs=e=>!Yn(e)||/github\.com/.test(e)?"GitHub":/bitbucket\.org/.test(e)?"Bitbucket":/gitlab\.com/.test(e)?"GitLab":/gitee\.com/.test(e)?"Gitee":null,Uh={GitHub:":repo/edit/:branch/:path",GitLab:":repo/-/edit/:branch/:path",Gitee:":repo/edit/:branch/:path",Bitbucket:":repo/src/:branch/:path?mode=edit&spa=0&at=:branch&fileviewer=file-view-default"},Kh=({docsRepo:e,editLinkPattern:t})=>{if(t)return t;const n=qs(e);return n!==null?Uh[n]:null},qh=({docsRepo:e,docsBranch:t,docsDir:n,filePathRelative:o,editLinkPattern:r})=>{if(!o)return null;const l=Kh({docsRepo:e,editLinkPattern:r});return l?l.replace(/:repo/,Yn(e)?e:`https://github.com/${e}`).replace(/:branch/,t).replace(/:path/,ds(`${fs(n)}/${o}`)):null},Gh={key:0,class:"navbar-items"},Yh=ue({__name:"NavbarItems",setup(e){const t=()=>{const u=Zt(),f=Jn(),h=ys(),m=Hr(),y=Yd(),k=je();return j(()=>{const x=Object.keys(h.value.locales);if(x.length<2)return[];const T=u.currentRoute.value.path,A=u.currentRoute.value.fullPath;return[{text:`${k.value.selectLanguageText}`,ariaLabel:`${k.value.selectLanguageAriaLabel??k.value.selectLanguageText}`,children:x.map(_=>{var P,U;const H=((P=h.value.locales)==null?void 0:P[_])??{},q=((U=y.value.locales)==null?void 0:U[_])??{},D=`${H.lang}`,b=q.selectLanguageName??D;let O;if(D===m.value.lang)O=A;else{const E=T.replace(f.value,_);u.getRoutes().some(N=>N.path===E)?O=A.replace(T,E):O=q.home??_}return{text:b,link:O}})}]})},n=()=>{const u=je(),f=j(()=>u.value.repo),h=j(()=>f.value?qs(f.value):null),m=j(()=>f.value&&!Yn(f.value)?`https://github.com/${f.value}`:f.value),y=j(()=>m.value?u.value.repoLabel?u.value.repoLabel:h.value===null?"Source":h.value:null);return j(()=>!m.value||!y.value?[]:[{text:y.value,link:m.value}])},o=u=>me(u)?Kr(u):u.children?{...u,children:u.children.map(o)}:u,r=()=>{const u=je();return j(()=>(u.value.navbar||[]).map(o))},l=be(!1),i=r(),s=t(),a=n(),c=j(()=>[...i.value,...s.value,...a.value]);return Ws(Hn.MOBILE,u=>{window.innerWidthc.value.length?(B(),ee("nav",Gh,[(B(!0),ee(ke,null,$t(c.value,h=>(B(),ee("div",{key:h.text,class:"navbar-item"},[h.children?(B(),Ae(Vh,{key:0,item:h,class:Ue(l.value?"mobile":"")},null,8,["item","class"])):(B(),Ae(bt,{key:1,item:h},null,8,["item"]))]))),128))])):Le("v-if",!0)}}),Gs=xe(Yh,[["__file","NavbarItems.vue"]]),Jh=["title"],Qh={class:"icon",focusable:"false",viewBox:"0 0 32 32"},Zh=$c(' ',9),Xh=[Zh],ep={class:"icon",focusable:"false",viewBox:"0 0 32 32"},tp=he("path",{d:"M13.502 5.414a15.075 15.075 0 0 0 11.594 18.194a11.113 11.113 0 0 1-7.975 3.39c-.138 0-.278.005-.418 0a11.094 11.094 0 0 1-3.2-21.584M14.98 3a1.002 1.002 0 0 0-.175.016a13.096 13.096 0 0 0 1.825 25.981c.164.006.328 0 .49 0a13.072 13.072 0 0 0 10.703-5.555a1.01 1.01 0 0 0-.783-1.565A13.08 13.08 0 0 1 15.89 4.38A1.015 1.015 0 0 0 14.98 3z",fill:"currentColor"},null,-1),np=[tp],op=ue({__name:"ToggleColorModeButton",setup(e){const t=je(),n=Ur(),o=()=>{n.value=!n.value};return(r,l)=>(B(),ee("button",{class:"toggle-color-mode-button",title:te(t).toggleColorMode,onClick:o},[_o((B(),ee("svg",Qh,Xh,512)),[[Co,!te(n)]]),_o((B(),ee("svg",ep,np,512)),[[Co,te(n)]])],8,Jh))}}),rp=xe(op,[["__file","ToggleColorModeButton.vue"]]),lp=["title"],ip=he("div",{class:"icon","aria-hidden":"true"},[he("span"),he("span"),he("span")],-1),sp=[ip],ap=ue({__name:"ToggleSidebarButton",emits:["toggle"],setup(e){const t=je();return(n,o)=>(B(),ee("div",{class:"toggle-sidebar-button",title:te(t).toggleSidebar,"aria-expanded":"false",role:"button",tabindex:"0",onClick:o[0]||(o[0]=r=>n.$emit("toggle"))},sp,8,lp))}}),cp=xe(ap,[["__file","ToggleSidebarButton.vue"]]),up=ue({__name:"Navbar",emits:["toggle-sidebar"],setup(e){const t=je(),n=be(null),o=be(null),r=be(0),l=j(()=>r.value?{maxWidth:r.value+"px"}:{});Ws(Hn.MOBILE,s=>{var c;const a=i(n.value,"paddingLeft")+i(n.value,"paddingRight");window.innerWidth {const c=_t("NavbarSearch");return B(),ee("header",{ref_key:"navbar",ref:n,class:"navbar"},[oe(cp,{onToggle:a[0]||(a[0]=u=>s.$emit("toggle-sidebar"))}),he("span",{ref_key:"navbarBrand",ref:o},[oe(Rh)],512),he("div",{class:"navbar-items-wrapper",style:Bn(l.value)},[we(s.$slots,"before"),oe(Gs,{class:"can-hide"}),we(s.$slots,"after"),te(t).colorModeSwitch?(B(),Ae(rp,{key:0})):Le("v-if",!0),oe(c)],4)],512)}}}),fp=xe(up,[["__file","Navbar.vue"]]),dp={class:"page-meta"},hp={key:0,class:"meta-item edit-link"},pp={key:1,class:"meta-item last-updated"},mp={class:"meta-item-label"},gp={class:"meta-item-info"},vp={key:2,class:"meta-item contributors"},bp={class:"meta-item-label"},_p={class:"meta-item-info"},yp=["title"],wp=ue({__name:"PageMeta",setup(e){const t=()=>{const a=je(),c=Yt(),u=vt();return j(()=>{if(!(u.value.editLink??a.value.editLink??!0))return null;const{repo:h,docsRepo:m=h,docsBranch:y="main",docsDir:k="",editLinkText:x}=a.value;if(!m)return null;const T=qh({docsRepo:m,docsBranch:y,docsDir:k,filePathRelative:c.value.filePathRelative,editLinkPattern:u.value.editLinkPattern??a.value.editLinkPattern});return T?{text:x??"Edit this page",link:T}:null})},n=()=>{const a=je(),c=Yt(),u=vt();return j(()=>{var m,y;return!(u.value.lastUpdated??a.value.lastUpdated??!0)||!((m=c.value.git)!=null&&m.updatedTime)?null:new Date((y=c.value.git)==null?void 0:y.updatedTime).toLocaleString()})},o=()=>{const a=je(),c=Yt(),u=vt();return j(()=>{var h;return u.value.contributors??a.value.contributors??!0?((h=c.value.git)==null?void 0:h.contributors)??null:null})},r=je(),l=t(),i=n(),s=o();return(a,c)=>{const u=_t("ClientOnly");return B(),ee("footer",dp,[te(l)?(B(),ee("div",hp,[oe(bt,{class:"meta-item-label",item:te(l)},null,8,["item"])])):Le("v-if",!0),te(i)?(B(),ee("div",pp,[he("span",mp,Re(te(r).lastUpdatedText)+": ",1),oe(u,null,{default:Me(()=>[he("span",gp,Re(te(i)),1)]),_:1})])):Le("v-if",!0),te(s)&&te(s).length?(B(),ee("div",vp,[he("span",bp,Re(te(r).contributorsText)+": ",1),he("span",_p,[(B(!0),ee(ke,null,$t(te(s),(f,h)=>(B(),ee(ke,{key:h},[he("span",{class:"contributor",title:`email: ${f.email}`},Re(f.name),9,yp),h!==te(s).length-1?(B(),ee(ke,{key:0},[Mt(", ")],64)):Le("v-if",!0)],64))),128))])])):Le("v-if",!0)])}}}),kp=xe(wp,[["__file","PageMeta.vue"]]),Ep={key:0,class:"page-nav"},Cp={class:"inner"},xp={key:0,class:"prev"},Lp={key:1,class:"next"},Tp=ue({__name:"PageNav",setup(e){const t=a=>a===!1?null:me(a)?Kr(a):Mr(a)?a:!1,n=(a,c,u)=>{const f=a.findIndex(h=>h.link===c);if(f!==-1){const h=a[f+u];return h!=null&&h.link?h:null}for(const h of a)if(h.children){const m=n(h.children,c,u);if(m)return m}return null},o=vt(),r=qr(),l=Xt(),i=j(()=>{const a=t(o.value.prev);return a!==!1?a:n(r.value,l.path,-1)}),s=j(()=>{const a=t(o.value.next);return a!==!1?a:n(r.value,l.path,1)});return(a,c)=>i.value||s.value?(B(),ee("nav",Ep,[he("p",Cp,[i.value?(B(),ee("span",xp,[oe(bt,{item:i.value},null,8,["item"])])):Le("v-if",!0),s.value?(B(),ee("span",Lp,[oe(bt,{item:s.value},null,8,["item"])])):Le("v-if",!0)])])):Le("v-if",!0)}}),Ap=xe(Tp,[["__file","PageNav.vue"]]),Sp={class:"page"},Pp={class:"theme-default-content"},Rp=ue({__name:"Page",setup(e){return(t,n)=>{const o=_t("Content");return B(),ee("main",Sp,[we(t.$slots,"top"),he("div",Pp,[we(t.$slots,"content-top"),oe(o),we(t.$slots,"content-bottom")]),oe(kp),oe(Ap),we(t.$slots,"bottom")])}}}),Ip=xe(Rp,[["__file","Page.vue"]]),Op=["onKeydown"],Np={class:"sidebar-item-children"},$p=ue({__name:"SidebarItem",props:{item:{type:Object,required:!0},depth:{type:Number,required:!1,default:0}},setup(e){const t=e,{item:n,depth:o}=Io(t),r=Xt(),l=Zt(),i=j(()=>Ks(n.value,r)),s=j(()=>({"sidebar-item":!0,"sidebar-heading":o.value===0,active:i.value,collapsible:n.value.collapsible})),a=j(()=>n.value.collapsible?i.value:!0),[c,u]=Md(a.value),f=m=>{n.value.collapsible&&(m.preventDefault(),u())},h=l.afterEach(m=>{Un(()=>{c.value=a.value})});return qn(()=>{h()}),(m,y)=>{var x;const k=_t("SidebarItem",!0);return B(),ee("li",null,[te(n).link?(B(),Ae(bt,{key:0,class:Ue(s.value),item:te(n)},null,8,["class","item"])):(B(),ee("p",{key:1,tabindex:"0",class:Ue(s.value),onClick:f,onKeydown:vu(f,["enter"])},[Mt(Re(te(n).text)+" ",1),te(n).collapsible?(B(),ee("span",{key:0,class:Ue(["arrow",te(c)?"down":"right"])},null,2)):Le("v-if",!0)],42,Op)),(x=te(n).children)!=null&&x.length?(B(),Ae(Us,{key:2},{default:Me(()=>[_o(he("ul",Np,[(B(!0),ee(ke,null,$t(te(n).children,T=>(B(),Ae(k,{key:`${te(o)}${T.text}${T.link}`,item:T,depth:te(o)+1},null,8,["item","depth"]))),128))],512),[[Co,te(c)]])]),_:1})):Le("v-if",!0)])}}}),Dp=xe($p,[["__file","SidebarItem.vue"]]),Mp={key:0,class:"sidebar-items"},Fp=ue({__name:"SidebarItems",setup(e){const t=Xt(),n=qr();return qe(()=>{ut(()=>t.hash,o=>{const r=document.querySelector(".sidebar");if(!r)return;const l=document.querySelector(`.sidebar a.sidebar-item[href="${t.path}${o}"]`);if(!l)return;const{top:i,height:s}=r.getBoundingClientRect(),{top:a,height:c}=l.getBoundingClientRect();ai+s&&l.scrollIntoView(!1)})}),(o,r)=>te(n).length?(B(),ee("ul",Mp,[(B(!0),ee(ke,null,$t(te(n),l=>(B(),Ae(Dp,{key:`${l.text}${l.link}`,item:l},null,8,["item"]))),128))])):Le("v-if",!0)}}),Hp=xe(Fp,[["__file","SidebarItems.vue"]]),zp={class:"sidebar"},Bp=ue({__name:"Sidebar",setup(e){return(t,n)=>(B(),ee("aside",zp,[oe(Gs),we(t.$slots,"top"),oe(Hp),we(t.$slots,"bottom")]))}}),jp=xe(Bp,[["__file","Sidebar.vue"]]),Vp=ue({__name:"Layout",setup(e){const t=Yt(),n=vt(),o=je(),r=j(()=>n.value.navbar!==!1&&o.value.navbar!==!1),l=qr(),i=be(!1),s=x=>{i.value=typeof x=="boolean"?x:!i.value},a={x:0,y:0},c=x=>{a.x=x.changedTouches[0].clientX,a.y=x.changedTouches[0].clientY},u=x=>{const T=x.changedTouches[0].clientX-a.x,A=x.changedTouches[0].clientY-a.y;Math.abs(T)>Math.abs(A)&&Math.abs(T)>40&&(T>0&&a.x<=80?s(!0):s(!1))},f=j(()=>[{"no-navbar":!r.value,"no-sidebar":!l.value.length,"sidebar-open":i.value},n.value.pageClass]);let h;qe(()=>{h=Zt().afterEach(()=>{s(!1)})}),Do(()=>{h()});const m=Bs(),y=m.resolve,k=m.pending;return(x,T)=>(B(),ee("div",{class:Ue(["theme-container",f.value]),onTouchstart:c,onTouchend:u},[we(x.$slots,"navbar",{},()=>[r.value?(B(),Ae(fp,{key:0,onToggleSidebar:s},{before:Me(()=>[we(x.$slots,"navbar-before")]),after:Me(()=>[we(x.$slots,"navbar-after")]),_:3})):Le("v-if",!0)]),he("div",{class:"sidebar-mask",onClick:T[0]||(T[0]=A=>s(!1))}),we(x.$slots,"sidebar",{},()=>[oe(jp,null,{top:Me(()=>[we(x.$slots,"sidebar-top")]),bottom:Me(()=>[we(x.$slots,"sidebar-bottom")]),_:3})]),we(x.$slots,"page",{},()=>[te(n).home?(B(),Ae(Sh,{key:0})):(B(),Ae(Gn,{key:1,name:"fade-slide-y",mode:"out-in",onBeforeEnter:te(y),onBeforeLeave:te(k)},{default:Me(()=>[(B(),Ae(Ip,{key:te(t).path},{top:Me(()=>[we(x.$slots,"page-top")]),"content-top":Me(()=>[we(x.$slots,"page-content-top")]),"content-bottom":Me(()=>[we(x.$slots,"page-content-bottom")]),bottom:Me(()=>[we(x.$slots,"page-bottom")]),_:3}))]),_:3},8,["onBeforeEnter","onBeforeLeave"]))])],34))}}),Wp=xe(Vp,[["__file","Layout.vue"]]),Up={class:"theme-container"},Kp={class:"page"},qp={class:"theme-default-content"},Gp=he("h1",null,"404",-1),Yp=ue({__name:"NotFound",setup(e){const t=Jn(),n=je(),o=n.value.notFound??["Not Found"],r=()=>o[Math.floor(Math.random()*o.length)],l=n.value.home??t.value,i=n.value.backToHome??"Back to home";return(s,a)=>{const c=_t("RouterLink");return B(),ee("div",Up,[he("main",Kp,[he("div",qp,[Gp,he("blockquote",null,Re(r()),1),oe(c,{to:te(l)},{default:Me(()=>[Mt(Re(te(i)),1)]),_:1},8,["to"])])])])}}}),Jp=xe(Yp,[["__file","NotFound.vue"]]);const Qp=wt({enhance({app:e,router:t}){e.component("Badge",kd),e.component("CodeGroup",Ed),e.component("CodeGroupItem",Td),e.component("AutoLinkExternalIcon",()=>{const o=e.component("ExternalLinkIcon");return o?ae(o):null}),e.component("NavbarSearch",()=>{const o=e.component("Docsearch")||e.component("SearchBox");return o?ae(o):null});const n=t.options.scrollBehavior;t.options.scrollBehavior=async(...o)=>(await Bs().wait(),n(...o))},setup(){Jd(),Xd()},layouts:{Layout:Wp,NotFound:Jp}}),Zp=e=>e instanceof Element?document.activeElement===e&&(["TEXTAREA","SELECT","INPUT"].includes(e.tagName)||e.hasAttribute("contenteditable")):!1,Xp=(e,t)=>t.some(n=>{if(me(n))return n===e.key;const{key:o,ctrl:r=!1,shift:l=!1,alt:i=!1}=n;return o===e.key&&r===e.ctrlKey&&l===e.shiftKey&&i===e.altKey}),em=/[^\x00-\x7F]/,tm=e=>e.split(/\s+/g).map(t=>t.trim()).filter(t=>!!t),ci=e=>e.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),ui=(e,t)=>{const n=t.join(" "),o=tm(e);if(em.test(e))return o.some(i=>n.toLowerCase().indexOf(i)>-1);const r=e.endsWith(" ");return new RegExp(o.map((i,s)=>o.length===s+1&&!r?`(?=.*\\b${ci(i)})`:`(?=.*\\b${ci(i)}\\b)`).join("")+".+","gi").test(n)},nm=({input:e,hotKeys:t})=>{if(t.value.length===0)return;const n=o=>{e.value&&Xp(o,t.value)&&!Zp(o.target)&&(o.preventDefault(),e.value.focus())};qe(()=>{document.addEventListener("keydown",n)}),qn(()=>{document.removeEventListener("keydown",n)})},om=[{title:"Apillon Wiki",headers:[{level:2,title:"Setup",slug:"setup",link:"#setup",children:[]},{level:2,title:"Development Server",slug:"development-server",link:"#development-server",children:[]}],path:"/",pathLocale:"/",extraFields:[]},{title:"",headers:[],path:"/",pathLocale:"/",extraFields:[]},{title:"Navigation",headers:[],path:"/about/1-navigation.html",pathLocale:"/",extraFields:[]},{title:"What is Apillon?",headers:[],path:"/about/2-what-is-apillon.html",pathLocale:"/",extraFields:[]},{title:"Why Apillon?",headers:[{level:2,title:"Unified pricing",slug:"unified-pricing",link:"#unified-pricing",children:[{level:3,title:"Predictable pricing for greater adoption",slug:"predictable-pricing-for-greater-adoption",link:"#predictable-pricing-for-greater-adoption",children:[]},{level:3,title:"Predictable scaling",slug:"predictable-scaling",link:"#predictable-scaling",children:[]},{level:3,title:"Token utility, volatility, and predictable pricing paradox",slug:"token-utility-volatility-and-predictable-pricing-paradox",link:"#token-utility-volatility-and-predictable-pricing-paradox",children:[]},{level:3,title:"Multiple services at a single price tag",slug:"multiple-services-at-a-single-price-tag",link:"#multiple-services-at-a-single-price-tag",children:[]}]}],path:"/about/3-why-apillon.html",pathLocale:"/",extraFields:[]},{title:"How does Apillon work?",headers:[{level:2,title:"APIs",slug:"apis",link:"#apis",children:[]},{level:2,title:"SDKs",slug:"sdks",link:"#sdks",children:[]},{level:2,title:"Documentation",slug:"documentation",link:"#documentation",children:[]},{level:2,title:"Monitoring",slug:"monitoring",link:"#monitoring",children:[]},{level:2,title:"Analytics",slug:"analytics",link:"#analytics",children:[]}],path:"/about/4-how-does-apillon-work.html",pathLocale:"/",extraFields:[]},{title:"Developing Web3 with Apillon",headers:[{level:2,title:"Challenges of blockchain complexity",slug:"challenges-of-blockchain-complexity",link:"#challenges-of-blockchain-complexity",children:[]},{level:2,title:"Web3 Development with and without Apillon",slug:"web3-development-with-and-without-apillon",link:"#web3-development-with-and-without-apillon",children:[]}],path:"/about/5-developing-web3-with-apillon.html",pathLocale:"/",extraFields:[]},{title:"Web3 and Apillon explained",headers:[{level:2,title:"Polkadot, a complete service stack for Web3 builders",slug:"polkadot-a-complete-service-stack-for-web3-builders",link:"#polkadot-a-complete-service-stack-for-web3-builders",children:[{level:3,title:"Evolution of blockchain applications",slug:"evolution-of-blockchain-applications",link:"#evolution-of-blockchain-applications",children:[]},{level:3,title:"Polkadot offers flexibility and more",slug:"polkadot-offers-flexibility-and-more",link:"#polkadot-offers-flexibility-and-more",children:[]},{level:3,title:"Room for improvement",slug:"room-for-improvement",link:"#room-for-improvement",children:[]}]},{level:2,title:"Glossary",slug:"glossary",link:"#glossary",children:[{level:3,title:"The world of Web3",slug:"the-world-of-web3",link:"#the-world-of-web3",children:[]},{level:3,title:"Polkadot Network",slug:"polkadot-network",link:"#polkadot-network",children:[]},{level:3,title:"The Apillon platform",slug:"the-apillon-platform",link:"#the-apillon-platform",children:[]},{level:3,title:"Read more",slug:"read-more",link:"#read-more",children:[]}]}],path:"/about/6-web3-up-close.html",pathLocale:"/",extraFields:[]},{title:"Community",headers:[{level:2,title:"Join Apillon Community",slug:"join-apillon-community",link:"#join-apillon-community",children:[]},{level:2,title:"Polkadot Treasury Proposal",slug:"polkadot-treasury-proposal",link:"#polkadot-treasury-proposal",children:[{level:3,title:"Common good and open source",slug:"common-good-and-open-source",link:"#common-good-and-open-source",children:[]},{level:3,title:"Proposal: Apillon, Polkadot, Web3",slug:"proposal-apillon-polkadot-web3",link:"#proposal-apillon-polkadot-web3",children:[]}]}],path:"/about/7-community.html",pathLocale:"/",extraFields:[]},{title:"NCTR token",headers:[{level:2,title:"Tokenomics Actors",slug:"tokenomics-actors",link:"#tokenomics-actors",children:[{level:3,title:"NCTR Developers",slug:"nctr-developers",link:"#nctr-developers",children:[]},{level:3,title:"NCTR Holders",slug:"nctr-holders",link:"#nctr-holders",children:[]}]},{level:2,title:"Token Utilities",slug:"token-utilities",link:"#token-utilities",children:[{level:3,title:"Governance",slug:"governance",link:"#governance",children:[]},{level:3,title:"Incentivization",slug:"incentivization",link:"#incentivization",children:[]},{level:3,title:"Payments",slug:"payments",link:"#payments",children:[]}]},{level:2,title:"Tokenomics",slug:"tokenomics",link:"#tokenomics",children:[{level:3,title:"Technical Token Parameters",slug:"technical-token-parameters",link:"#technical-token-parameters",children:[]},{level:3,title:"Tokenomics Whitepaper",slug:"tokenomics-whitepaper",link:"#tokenomics-whitepaper",children:[]}]}],path:"/about/8-nctr-token.html",pathLocale:"/",extraFields:[]},{title:"Apillon API",headers:[{level:2,title:"Endpoints",slug:"endpoints",link:"#endpoints",children:[]},{level:2,title:"API to Web3",slug:"api-to-web3",link:"#api-to-web3",children:[{level:3,title:"Requests",slug:"requests",link:"#requests",children:[]},{level:3,title:"Authentication and authorization",slug:"authentication-and-authorization",link:"#authentication-and-authorization",children:[]},{level:3,title:"Listing requests",slug:"listing-requests",link:"#listing-requests",children:[]},{level:3,title:"Responses",slug:"responses",link:"#responses",children:[]},{level:3,title:"Error handling",slug:"error-handling",link:"#error-handling",children:[]},{level:3,title:"Common errors",slug:"common-errors",link:"#common-errors",children:[]}]},{level:2,title:"Project",slug:"project",link:"#project",children:[{level:3,title:"Credit balance",slug:"credit-balance",link:"#credit-balance",children:[]}]},{level:2,title:"API Code Examples",slug:"api-code-examples",link:"#api-code-examples",children:[]}],path:"/build/1-apillon-api.html",pathLocale:"/",extraFields:[]},{title:"Apillon Flutter SDK",headers:[{level:2,title:"Requirements",slug:"requirements",link:"#requirements",children:[]},{level:2,title:"Getting started",slug:"getting-started",link:"#getting-started",children:[{level:3,title:"Initialization",slug:"initialization",link:"#initialization",children:[]}]},{level:2,title:"Modules",slug:"modules",link:"#modules",children:[{level:3,title:"Storage",slug:"storage",link:"#storage",children:[]},{level:3,title:"NFTs",slug:"nfts",link:"#nfts",children:[]},{level:3,title:"Identity",slug:"identity",link:"#identity",children:[]}]}],path:"/build/10-flutter-sdk.html",pathLocale:"/",extraFields:[]},{title:"Storage API",headers:[{level:3,title:"List buckets",slug:"list-buckets",link:"#list-buckets",children:[]},{level:3,title:"Create new bucket",slug:"create-new-bucket",link:"#create-new-bucket",children:[]},{level:3,title:"Upload to bucket",slug:"upload-to-bucket",link:"#upload-to-bucket",children:[]},{level:3,title:"End upload session",slug:"end-upload-session",link:"#end-upload-session",children:[]},{level:3,title:"List bucket content",slug:"list-bucket-content",link:"#list-bucket-content",children:[]},{level:3,title:"List files",slug:"list-files",link:"#list-files",children:[]},{level:3,title:"Get file details",slug:"get-file-details",link:"#get-file-details",children:[]},{level:3,title:"Delete file",slug:"delete-file",link:"#delete-file",children:[]},{level:3,title:"Delete directory",slug:"delete-directory",link:"#delete-directory",children:[]},{level:3,title:"Get storage info",slug:"get-storage-info",link:"#get-storage-info",children:[]},{level:3,title:"Get IPFS cluster info",slug:"get-ipfs-cluster-info",link:"#get-ipfs-cluster-info",children:[]},{level:3,title:"Get or generate link for IPFS",slug:"get-or-generate-link-for-ipfs",link:"#get-or-generate-link-for-ipfs",children:[]},{level:2,title:"IPNS",slug:"ipns",link:"#ipns",children:[{level:3,title:"List IPNS names",slug:"list-ipns-names",link:"#list-ipns-names",children:[]},{level:3,title:"Create new IPNS",slug:"create-new-ipns",link:"#create-new-ipns",children:[]},{level:3,title:"Get IPNS",slug:"get-ipns",link:"#get-ipns",children:[]},{level:3,title:"Publish IPNS",slug:"publish-ipns",link:"#publish-ipns",children:[]},{level:3,title:"Delete IPNS",slug:"delete-ipns",link:"#delete-ipns",children:[]}]}],path:"/build/2-storage-api.html",pathLocale:"/",extraFields:[]},{title:"Hosting API",headers:[{level:3,title:"List websites",slug:"list-websites",link:"#list-websites",children:[]},{level:3,title:"Get website",slug:"get-website",link:"#get-website",children:[]},{level:3,title:"Get URLs for files upload",slug:"get-urls-for-files-upload",link:"#get-urls-for-files-upload",children:[]},{level:3,title:"End upload session",slug:"end-upload-session",link:"#end-upload-session",children:[]},{level:3,title:"Deploy website",slug:"deploy-website",link:"#deploy-website",children:[]},{level:3,title:"List website deployments",slug:"list-website-deployments",link:"#list-website-deployments",children:[]},{level:3,title:"Get deployment",slug:"get-deployment",link:"#get-deployment",children:[]}],path:"/build/3-hosting-api.html",pathLocale:"/",extraFields:[]},{title:"NFTs API",headers:[{level:3,title:"Get NFT Collection",slug:"get-nft-collection",link:"#get-nft-collection",children:[]},{level:3,title:"List NFT Collections",slug:"list-nft-collections",link:"#list-nft-collections",children:[]},{level:3,title:"List Collection Transactions",slug:"list-collection-transactions",link:"#list-collection-transactions",children:[]},{level:3,title:"Create NFT Collection",slug:"create-nft-collection",link:"#create-nft-collection",children:[]},{level:3,title:"Transfer Collection",slug:"transfer-collection",link:"#transfer-collection",children:[]},{level:3,title:"Mint Collection NFTs",slug:"mint-collection-nfts",link:"#mint-collection-nfts",children:[]},{level:3,title:"Nest Mint Collection NFTs",slug:"nest-mint-collection-nfts",link:"#nest-mint-collection-nfts",children:[]},{level:3,title:"Burn Collection NFT",slug:"burn-collection-nft",link:"#burn-collection-nft",children:[]}],path:"/build/4-nfts-api.html",pathLocale:"/",extraFields:[]},{title:"Apillon SDK",headers:[{level:2,title:"Requirements",slug:"requirements",link:"#requirements",children:[]},{level:2,title:"Getting started",slug:"getting-started",link:"#getting-started",children:[{level:3,title:"Installation",slug:"installation",link:"#installation",children:[]},{level:3,title:"Initialization",slug:"initialization",link:"#initialization",children:[]},{level:3,title:"Detailed docs",slug:"detailed-docs",link:"#detailed-docs",children:[]},{level:3,title:"Examples",slug:"examples",link:"#examples",children:[]}]},{level:2,title:"Hosting",slug:"hosting",link:"#hosting",children:[{level:3,title:"Usage example",slug:"usage-example",link:"#usage-example",children:[]}]},{level:2,title:"Storage",slug:"storage",link:"#storage",children:[{level:3,title:"Usage example",slug:"usage-example-1",link:"#usage-example-1",children:[]},{level:3,title:"IPNS methods",slug:"ipns-methods",link:"#ipns-methods",children:[]}]},{level:2,title:"NFTs",slug:"nfts",link:"#nfts",children:[{level:3,title:"Usage example",slug:"usage-example-2",link:"#usage-example-2",children:[]}]},{level:2,title:"Identity",slug:"identity",link:"#identity",children:[{level:3,title:"Usage example",slug:"usage-example-3",link:"#usage-example-3",children:[]}]},{level:2,title:"Computing",slug:"computing",link:"#computing",children:[{level:3,title:"Usage example",slug:"usage-example-4",link:"#usage-example-4",children:[]}]},{level:2,title:"Social",slug:"social",link:"#social",children:[{level:3,title:"Usage example",slug:"usage-example-5",link:"#usage-example-5",children:[]}]}],path:"/build/5-apillon-sdk.html",pathLocale:"/",extraFields:[]},{title:"Apillon CLI",headers:[{level:2,title:"Requirements",slug:"requirements",link:"#requirements",children:[]},{level:2,title:"Installation",slug:"installation",link:"#installation",children:[{level:3,title:"Global Options",slug:"global-options",link:"#global-options",children:[]},{level:3,title:"Environment Variables",slug:"environment-variables",link:"#environment-variables",children:[]},{level:3,title:"Help",slug:"help",link:"#help",children:[]},{level:3,title:"List pagination options",slug:"list-pagination-options",link:"#list-pagination-options",children:[]}]},{level:2,title:"Hosting Commands",slug:"hosting-commands",link:"#hosting-commands",children:[]},{level:2,title:"Storage Commands",slug:"storage-commands",link:"#storage-commands",children:[{level:3,title:"IPNS Commands",slug:"ipns-commands",link:"#ipns-commands",children:[]}]},{level:2,title:"NFT Commands",slug:"nft-commands",link:"#nft-commands",children:[]},{level:2,title:"Using in CI/CD tools",slug:"using-in-ci-cd-tools",link:"#using-in-ci-cd-tools",children:[{level:3,title:"Deploying websites",slug:"deploying-websites",link:"#deploying-websites",children:[]}]}],path:"/build/6-apillon-cli.html",pathLocale:"/",extraFields:[]},{title:"Apillon OAuth Integration",headers:[{level:2,title:"Client - OAuth popup & events",slug:"client-oauth-popup-events",link:"#client-oauth-popup-events",children:[]},{level:2,title:"Server - Auth API endpoints",slug:"server-auth-api-endpoints",link:"#server-auth-api-endpoints",children:[{level:3,title:"Obtain a session token",slug:"obtain-a-session-token",link:"#obtain-a-session-token",children:[]},{level:3,title:"Verify user login",slug:"verify-user-login",link:"#verify-user-login",children:[]}]}],path:"/build/7-apillon-oauth-integration.html",pathLocale:"/",extraFields:[]},{title:"Computing API",headers:[{level:2,title:"Create Computing Contract",slug:"create-computing-contract",link:"#create-computing-contract",children:[]},{level:2,title:"List Computing Contracts",slug:"list-computing-contracts",link:"#list-computing-contracts",children:[]},{level:2,title:"Get Computing Contract",slug:"get-computing-contract",link:"#get-computing-contract",children:[]},{level:2,title:"List Contract Transactions",slug:"list-contract-transactions",link:"#list-contract-transactions",children:[]},{level:2,title:"Transfer Ownership",slug:"transfer-ownership",link:"#transfer-ownership",children:[]},{level:2,title:"Encrypt Content",slug:"encrypt-content",link:"#encrypt-content",children:[]},{level:2,title:"Assign CID to NFT",slug:"assign-cid-to-nft",link:"#assign-cid-to-nft",children:[]}],path:"/build/8-computing-api.html",pathLocale:"/",extraFields:[]},{title:"Social API",headers:[{level:2,title:"Channels",slug:"channels",link:"#channels",children:[{level:3,title:"List channels",slug:"list-channels",link:"#list-channels",children:[]},{level:3,title:"Get channel",slug:"get-channel",link:"#get-channel",children:[]},{level:3,title:"Create channel",slug:"create-channel",link:"#create-channel",children:[]}]},{level:2,title:"Hubs",slug:"hubs",link:"#hubs",children:[{level:3,title:"List hubs",slug:"list-hubs",link:"#list-hubs",children:[]},{level:3,title:"Get hub",slug:"get-hub",link:"#get-hub",children:[]},{level:3,title:"Create hub",slug:"create-hub",link:"#create-hub",children:[]}]}],path:"/build/9-social-api.html",pathLocale:"/",extraFields:[]},{title:"Good to know",headers:[{level:2,title:"Concepts",slug:"concepts",link:"#concepts",children:[{level:3,title:"Centralized vs. decentralized",slug:"centralized-vs-decentralized",link:"#centralized-vs-decentralized",children:[]},{level:3,title:"Production vs. Beta",slug:"production-vs-beta",link:"#production-vs-beta",children:[]}]},{level:2,title:"Terminology and underlying technology",slug:"terminology-and-underlying-technology",link:"#terminology-and-underlying-technology",children:[{level:3,title:"IPFS",slug:"ipfs",link:"#ipfs",children:[]},{level:3,title:"Crust Network",slug:"crust-network",link:"#crust-network",children:[]},{level:3,title:"Storage buckets",slug:"storage-buckets",link:"#storage-buckets",children:[]}]}],path:"/web3-services/1-good-to-know.html",pathLocale:"/",extraFields:[]},{title:"Web3 Storage",headers:[{level:2,title:"Storage bucket",slug:"storage-bucket",link:"#storage-bucket",children:[]},{level:2,title:"File storage",slug:"file-storage",link:"#file-storage",children:[]},{level:2,title:"File deletion",slug:"file-deletion",link:"#file-deletion",children:[]}],path:"/web3-services/2-web3-storage.html",pathLocale:"/",extraFields:[]},{title:"Web3 Hosting",headers:[{level:2,title:"Website/app hosting",slug:"website-app-hosting",link:"#website-app-hosting",children:[]},{level:2,title:"Deployment",slug:"deployment",link:"#deployment",children:[]},{level:2,title:"File deletion",slug:"file-deletion",link:"#file-deletion",children:[]}],path:"/web3-services/3-web3-hosting.html",pathLocale:"/",extraFields:[]},{title:"NFTs",headers:[{level:2,title:"NFT files",slug:"nft-files",link:"#nft-files",children:[]},{level:2,title:"NFT metadata",slug:"nft-metadata",link:"#nft-metadata",children:[]},{level:2,title:"NFT deployment",slug:"nft-deployment",link:"#nft-deployment",children:[]}],path:"/web3-services/4-nfts.html",pathLocale:"/",extraFields:[]},{title:"Web3 Authentication",headers:[{level:2,title:"Authentication workflow",slug:"authentication-workflow",link:"#authentication-workflow",children:[]},{level:2,title:"Apillon Open Authentication (OAuth)",slug:"apillon-open-authentication-oauth",link:"#apillon-open-authentication-oauth",children:[]}],path:"/web3-services/5-web3-authentication.html",pathLocale:"/",extraFields:[]},{title:"Web3 Social",headers:[{level:2,title:"Chat",slug:"chat",link:"#chat",children:[]}],path:"/web3-services/6-web3-social.html",pathLocale:"/",extraFields:[]},{title:"Web3 Compute power",headers:[{level:2,title:"NFT gated file access",slug:"nft-gated-file-access",link:"#nft-gated-file-access",children:[]}],path:"/web3-services/7-web3-compute.html",pathLocale:"/",extraFields:[]},{title:"",headers:[],path:"/404.html",pathLocale:"/",extraFields:[]}],rm=be(om),lm=()=>rm,im=({searchIndex:e,routeLocale:t,query:n,maxSuggestions:o})=>{const r=j(()=>e.value.filter(l=>l.pathLocale===t.value));return j(()=>{const l=n.value.trim().toLowerCase();if(!l)return[];const i=[],s=(a,c)=>{ui(l,[c.title])&&i.push({link:`${a.path}#${c.slug}`,title:a.title,header:c.title});for(const u of c.children){if(i.length>=o.value)return;s(a,u)}};for(const a of r.value){if(i.length>=o.value)break;if(ui(l,[a.title,...a.extraFields])){i.push({link:a.path,title:a.title});continue}for(const c of a.headers){if(i.length>=o.value)break;s(a,c)}}return i})},sm=e=>{const t=be(0);return{focusIndex:t,focusNext:()=>{t.value{t.value>0?t.value-=1:t.value=e.value.length-1}}},am=ue({name:"SearchBox",props:{locales:{type:Object,required:!1,default:()=>({})},hotKeys:{type:Array,required:!1,default:()=>[]},maxSuggestions:{type:Number,required:!1,default:5}},setup(e){const{locales:t,hotKeys:n,maxSuggestions:o}=Io(e),r=Zt(),l=Jn(),i=lm(),s=be(null),a=be(!1),c=be(""),u=j(()=>t.value[l.value]??{}),f=im({searchIndex:i,routeLocale:l,query:c,maxSuggestions:o}),{focusIndex:h,focusNext:m,focusPrev:y}=sm(f);nm({input:s,hotKeys:n});const k=j(()=>a.value&&!!f.value.length),x=()=>{k.value&&y()},T=()=>{k.value&&m()},A=v=>{if(!k.value)return;const _=f.value[v];_&&r.push(_.link).then(()=>{c.value="",h.value=0})};return()=>ae("form",{class:"search-box",role:"search"},[ae("input",{ref:s,type:"search",placeholder:u.value.placeholder,autocomplete:"off",spellcheck:!1,value:c.value,onFocus:()=>a.value=!0,onBlur:()=>a.value=!1,onInput:v=>c.value=v.target.value,onKeydown:v=>{switch(v.key){case"ArrowUp":{x();break}case"ArrowDown":{T();break}case"Enter":{v.preventDefault(),A(h.value);break}}}}),k.value&&ae("ul",{class:"suggestions",onMouseleave:()=>h.value=-1},f.value.map(({link:v,title:_,header:H},q)=>ae("li",{class:["suggestion",{focus:h.value===q}],onMouseenter:()=>h.value=q,onMousedown:()=>A(q)},ae("a",{href:v,onClick:D=>D.preventDefault()},[ae("span",{class:"page-title"},_),H&&ae("span",{class:"page-header"},`> ${H}`)]))))])}});const cm={"/":{placeholder:"Search"}},um=["s","/"],fm=5,dm=wt({enhance({app:e}){e.component("SearchBox",t=>ae(am,{locales:cm,hotKeys:um,maxSuggestions:fm,...t}))}}),hm={enhance:({app:e})=>{e.component("CodeDiv",ge(()=>Y(()=>import("./CodeDiv-2b1383e9.js"),[])))}},pm=wt({enhance({app:e,router:t,siteData:n}){t.beforeEach((o,r,l)=>{const s={"/build/2-web3-services.html#storage-bucket":"/web3-services/2-web3-storage.html#storage-bucket","/build/2-web3-services.html#web3-hosting":"/web3-services/3-web3-hosting.html","/build/3-apillon-api.html#web3-hosting-api":"/build/3-hosting-api.html","/build/3-apillon-api.html#web3-storage-api":"/build/2-storage-api.html","/build/3-apillon-api.html":"/build/1-apillon-api.html","/build/3-apillon-api.html#api-to-web3":"/build/1-apillon-api.html","/build/#concepts":"/web3-services/1-good-to-know.html#concepts","/build/2-web3-services.html#web3-storage":"/web3-services/2-web3-storage.html","/build/2-web3-services.html#nfts":"/web3-services/4-nfts.html","/build/#ipfs":"/web3-services/1-good-to-know.html#ipfs"}[o.fullPath];s?l({path:s}):l()})}}),uo=[Kf,Yf,Xf,fd,md,yd,Qp,dm,hm,pm],mm=[["v-8daa1a0e","/",{title:"Apillon Wiki"},["/README.md"]],["v-8daa1a0e","/",{title:""},["/index.md"]],["v-92c536e4","/about/1-navigation.html",{title:"Navigation"},[":md"]],["v-d64fcca0","/about/2-what-is-apillon.html",{title:"What is Apillon?"},[":md"]],["v-0f48940c","/about/3-why-apillon.html",{title:"Why Apillon?"},[":md"]],["v-6bda2dde","/about/4-how-does-apillon-work.html",{title:"How does Apillon work?"},[":md"]],["v-ac15b5a4","/about/5-developing-web3-with-apillon.html",{title:"Developing Web3 with Apillon"},[":md"]],["v-da41cf52","/about/6-web3-up-close.html",{title:"Web3 and Apillon explained"},[":md"]],["v-929e013e","/about/7-community.html",{title:"Community"},[":md"]],["v-564790ea","/about/8-nctr-token.html",{title:"NCTR token"},[":md"]],["v-cdfbcdae","/build/1-apillon-api.html",{title:"Apillon API"},[":md"]],["v-4d91ab64","/build/10-flutter-sdk.html",{title:"Apillon Flutter SDK"},[":md"]],["v-1da918f4","/build/2-storage-api.html",{title:"Storage API"},[":md"]],["v-3fe44474","/build/3-hosting-api.html",{title:"Hosting API"},[":md"]],["v-ae3a6dfc","/build/4-nfts-api.html",{title:"NFTs API"},[":md"]],["v-fc8498f6","/build/5-apillon-sdk.html",{title:"Apillon SDK"},[":md"]],["v-6c04f07e","/build/6-apillon-cli.html",{title:"Apillon CLI"},[":md"]],["v-15f6e7df","/build/7-apillon-oauth-integration.html",{title:"Apillon OAuth Integration"},[":md"]],["v-6b6a7fe7","/build/8-computing-api.html",{title:"Computing API"},[":md"]],["v-6ef47b51","/build/9-social-api.html",{title:"Social API"},[":md"]],["v-2d5fd574","/web3-services/1-good-to-know.html",{title:"Good to know"},[":md"]],["v-47991eb5","/web3-services/2-web3-storage.html",{title:"Web3 Storage"},[":md"]],["v-f2d17dd2","/web3-services/3-web3-hosting.html",{title:"Web3 Hosting"},[":md"]],["v-713d4f4d","/web3-services/4-nfts.html",{title:"NFTs"},[":md"]],["v-662ed367","/web3-services/5-web3-authentication.html",{title:"Web3 Authentication"},[":md"]],["v-592c909e","/web3-services/6-web3-social.html",{title:"Web3 Social"},[":md"]],["v-b6f01184","/web3-services/7-web3-compute.html",{title:"Web3 Compute power"},[":md"]],["v-3706649a","/404.html",{title:""},[]]];var fi=ue({name:"Vuepress",setup(){const e=Ou();return()=>ae(e.value)}}),gm=()=>mm.reduce((e,[t,n,o,r])=>(e.push({name:t,path:n,component:fi,meta:o},{path:n.endsWith("/")?n+"index.html":n.substring(0,n.length-5),redirect:n},...r.map(l=>({path:l===":md"?n.substring(0,n.length-5)+".md":l,redirect:n}))),e),[{name:"404",path:"/:catchAll(.*)",component:fi}]),vm=tf,bm=()=>{const e=Hf({history:vm(fs("/")),routes:gm(),scrollBehavior:(t,n,o)=>o||(t.hash?{el:t.hash}:{top:0})});return e.beforeResolve(async(t,n)=>{var o;(t.path!==n.path||n===pt)&&([Pt.value]=await Promise.all([ht.resolvePageData(t.name),(o=ps[t.name])==null?void 0:o.__asyncLoader()]))}),e},_m=e=>{e.component("ClientOnly",zr),e.component("Content",Mu)},ym=(e,t,n)=>{const o=j(()=>ht.resolveLayouts(n)),r=li(()=>t.currentRoute.value.path),l=li(()=>ht.resolveRouteLocale(rn.value.locales,r.value)),i=j(()=>ht.resolveSiteLocaleData(rn.value,l.value)),s=j(()=>ht.resolvePageFrontmatter(Pt.value)),a=j(()=>ht.resolvePageHeadTitle(Pt.value,i.value)),c=j(()=>ht.resolvePageHead(a.value,s.value,i.value)),u=j(()=>ht.resolvePageLang(Pt.value,i.value)),f=j(()=>ht.resolvePageLayout(Pt.value,o.value));return e.provide(Au,o),e.provide(gs,s),e.provide(Ru,a),e.provide(vs,c),e.provide(bs,u),e.provide(_s,f),e.provide(Fr,l),e.provide(ws,i),Object.defineProperties(e.config.globalProperties,{$frontmatter:{get:()=>s.value},$head:{get:()=>c.value},$headTitle:{get:()=>a.value},$lang:{get:()=>u.value},$page:{get:()=>Pt.value},$routeLocale:{get:()=>l.value},$site:{get:()=>rn.value},$siteLocale:{get:()=>i.value},$withBase:{get:()=>Br}}),{layouts:o,pageData:Pt,pageFrontmatter:s,pageHead:c,pageHeadTitle:a,pageLang:u,pageLayout:f,routeLocale:l,siteData:rn,siteLocaleData:i}},wm=()=>{const e=Pu(),t=Iu(),n=be([]),o=()=>{e.value.forEach(l=>{const i=km(l);i&&n.value.push(i)})},r=()=>{document.documentElement.lang=t.value,n.value.forEach(l=>{l.parentNode===document.head&&document.head.removeChild(l)}),n.value.splice(0,n.value.length),e.value.forEach(l=>{const i=Em(l);i!==null&&(document.head.appendChild(i),n.value.push(i))})};qt(Nu,r),qe(()=>{o(),r(),ut(()=>e.value,r)})},km=([e,t,n=""])=>{const o=Object.entries(t).map(([s,a])=>me(a)?`[${s}=${JSON.stringify(a)}]`:a===!0?`[${s}]`:"").join(""),r=`head > ${e}${o}`;return Array.from(document.querySelectorAll(r)).find(s=>s.innerText===n)||null},Em=([e,t,n])=>{if(!me(e))return null;const o=document.createElement(e);return Mr(t)&&Object.entries(t).forEach(([r,l])=>{me(l)?o.setAttribute(r,l):l===!0&&o.setAttribute(r,"")}),me(n)&&o.appendChild(document.createTextNode(n)),o},Cm=yu,xm=async()=>{var n;const e=Cm({name:"VuepressApp",setup(){var o;wm();for(const r of uo)(o=r.setup)==null||o.call(r);return()=>[ae(Os),...uo.flatMap(({rootComponents:r=[]})=>r.map(l=>ae(l)))]}}),t=bm();_m(e),ym(e,t,uo);for(const o of uo)await((n=o.enhance)==null?void 0:n.call(o,{app:e,router:t,siteData:rn}));return e.use(t),{app:e,router:t}};xm().then(({app:e,router:t})=>{t.isReady().then(()=>{e.mount("#app")})});export{xe as _,$c as a,he as b,ee as c,xm as createVueApp,oe as d,Mt as e,Le as f,we as g,B as o,_t as r,Me as w}; diff --git a/assets/arrow-right.svg b/assets/arrow-right.svg new file mode 100644 index 00000000..6e5eb4ae --- /dev/null +++ b/assets/arrow-right.svg @@ -0,0 +1,3 @@ + diff --git a/assets/authtrail-nino-kutnjak-new-chief-product-owner.png b/assets/authtrail-nino-kutnjak-new-chief-product-owner.png new file mode 100644 index 00000000..edae8ed7 Binary files /dev/null and b/assets/authtrail-nino-kutnjak-new-chief-product-owner.png differ diff --git a/assets/back-to-top-8efcbe56.svg b/assets/back-to-top-8efcbe56.svg new file mode 100644 index 00000000..83236781 --- /dev/null +++ b/assets/back-to-top-8efcbe56.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/homepage.svg b/assets/homepage.svg new file mode 100644 index 00000000..31e3d056 --- /dev/null +++ b/assets/homepage.svg @@ -0,0 +1,2219 @@ + + + diff --git a/assets/index.html-7df57863.js b/assets/index.html-7df57863.js new file mode 100644 index 00000000..d24a3c49 --- /dev/null +++ b/assets/index.html-7df57863.js @@ -0,0 +1 @@ +import{_ as t,o as a,c as i,a as s}from"./app-270cc6ec.js";const e="/assets/arrow-right.svg",o={},c=s(' ',1),l=[c];function n(r,h){return a(),i("div",null,l)}const p=t(o,[["render",n],["__file","index.html.vue"]]);export{p as default}; diff --git a/assets/index.html-d0c26fab.js b/assets/index.html-d0c26fab.js new file mode 100644 index 00000000..ba0ec319 --- /dev/null +++ b/assets/index.html-d0c26fab.js @@ -0,0 +1 @@ +const a=JSON.parse('{"key":"v-8daa1a0e","path":"/","title":"","lang":"en-US","frontmatter":{"pageClass":"homepage","lastUpdated":false,"editLink":false},"headers":[],"git":{"updatedTime":1715328683000,"contributors":[{"name":"Damjan Dimitrov","email":"damjandimitrov1@gmail.com","commits":1}]},"filePathRelative":"index.md"}');export{a as data}; diff --git a/assets/logo-dark.svg b/assets/logo-dark.svg new file mode 100644 index 00000000..5056f46b --- /dev/null +++ b/assets/logo-dark.svg @@ -0,0 +1,10 @@ + diff --git a/assets/logo-favicon-light.png b/assets/logo-favicon-light.png new file mode 100644 index 00000000..e782bc39 Binary files /dev/null and b/assets/logo-favicon-light.png differ diff --git a/assets/logo-favicon.png b/assets/logo-favicon.png new file mode 100644 index 00000000..3d409a1e Binary files /dev/null and b/assets/logo-favicon.png differ diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 00000000..3d409a1e Binary files /dev/null and b/assets/logo.png differ diff --git a/assets/logo.svg b/assets/logo.svg new file mode 100644 index 00000000..61f8c2a7 --- /dev/null +++ b/assets/logo.svg @@ -0,0 +1,10 @@ + diff --git a/assets/search-0782d0d1.svg b/assets/search-0782d0d1.svg new file mode 100644 index 00000000..03d83913 --- /dev/null +++ b/assets/search-0782d0d1.svg @@ -0,0 +1 @@ + diff --git a/assets/sidebar-minus.svg b/assets/sidebar-minus.svg new file mode 100644 index 00000000..c5da15fb --- /dev/null +++ b/assets/sidebar-minus.svg @@ -0,0 +1,3 @@ + diff --git a/assets/sidebar-plus.svg b/assets/sidebar-plus.svg new file mode 100644 index 00000000..45a845cb --- /dev/null +++ b/assets/sidebar-plus.svg @@ -0,0 +1,3 @@ + diff --git a/assets/style-79667658.css b/assets/style-79667658.css new file mode 100644 index 00000000..8e04831c --- /dev/null +++ b/assets/style-79667658.css @@ -0,0 +1 @@ +@import"https://fonts.googleapis.com/css2?family=Inter:wght@300;900&display=swap";@import"https://fonts.googleapis.com/css2?family=Inter:wght@200;300;900&display=swap";:root{--back-to-top-z-index: 5;--back-to-top-color: #3eaf7c;--back-to-top-color-hover: #71cda3}.back-to-top{cursor:pointer;position:fixed;bottom:2rem;right:2.5rem;width:2rem;height:1.2rem;background-color:var(--back-to-top-color);-webkit-mask:url(/assets/back-to-top-8efcbe56.svg) no-repeat;mask:url(/assets/back-to-top-8efcbe56.svg) no-repeat;z-index:var(--back-to-top-z-index)}.back-to-top:hover{background-color:var(--back-to-top-color-hover)}@media (max-width: 959px){.back-to-top{display:none}}@media print{.back-to-top{display:none}}.back-to-top-enter-active,.back-to-top-leave-active{transition:opacity .3s}.back-to-top-enter-from,.back-to-top-leave-to{opacity:0}:root{--external-link-icon-color: #aaa}.external-link-icon{position:relative;display:inline-block;color:var(--external-link-icon-color);vertical-align:middle;top:-1px}@media print{.external-link-icon{display:none}}.external-link-icon-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}:root{--medium-zoom-z-index: 100;--medium-zoom-bg-color: #ffffff;--medium-zoom-opacity: 1}.medium-zoom-overlay{background-color:var(--medium-zoom-bg-color)!important;z-index:var(--medium-zoom-z-index)}.medium-zoom-overlay~img{z-index:calc(var(--medium-zoom-z-index) + 1)}.medium-zoom--opened .medium-zoom-overlay{opacity:var(--medium-zoom-opacity)}:root{--nprogress-color: #29d;--nprogress-z-index: 1031}#nprogress{pointer-events:none}#nprogress .bar{background:var(--nprogress-color);position:fixed;z-index:var(--nprogress-z-index);top:0;left:0;width:100%;height:2px}:root{--c-brand: #3eaf7c;--c-brand-light: #4abf8a;--c-bg: #ffffff;--c-bg-light: #f3f4f5;--c-bg-lighter: #eeeeee;--c-bg-dark: #ebebec;--c-bg-darker: #e6e6e6;--c-bg-navbar: var(--c-bg);--c-bg-sidebar: var(--c-bg);--c-bg-arrow: #cccccc;--c-text: #2c3e50;--c-text-accent: var(--c-brand);--c-text-light: #3a5169;--c-text-lighter: #4e6e8e;--c-text-lightest: #6a8bad;--c-text-quote: #999999;--c-border: #eaecef;--c-border-dark: #dfe2e5;--c-tip: #42b983;--c-tip-bg: var(--c-bg-light);--c-tip-title: var(--c-text);--c-tip-text: var(--c-text);--c-tip-text-accent: var(--c-text-accent);--c-warning: #ffc310;--c-warning-bg: #fffae3;--c-warning-bg-light: #fff3ba;--c-warning-bg-lighter: #fff0b0;--c-warning-border-dark: #f7dc91;--c-warning-details-bg: #fff5ca;--c-warning-title: #f1b300;--c-warning-text: #746000;--c-warning-text-accent: #edb100;--c-warning-text-light: #c1971c;--c-warning-text-quote: #ccab49;--c-danger: #f11e37;--c-danger-bg: #ffe0e0;--c-danger-bg-light: #ffcfde;--c-danger-bg-lighter: #ffc9c9;--c-danger-border-dark: #f1abab;--c-danger-details-bg: #ffd4d4;--c-danger-title: #ed1e2c;--c-danger-text: #660000;--c-danger-text-accent: #bd1a1a;--c-danger-text-light: #b5474d;--c-danger-text-quote: #c15b5b;--c-details-bg: #eeeeee;--c-badge-tip: var(--c-tip);--c-badge-warning: #ecc808;--c-badge-warning-text: var(--c-bg);--c-badge-danger: #dc2626;--c-badge-danger-text: var(--c-bg);--t-color: .3s ease;--t-transform: .3s ease;--code-bg-color: #282c34;--code-hl-bg-color: rgba(0, 0, 0, .66);--code-ln-color: #9e9e9e;--code-ln-wrapper-width: 3.5rem;--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;--font-family-code: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace;--navbar-height: 3.6rem;--navbar-padding-v: .7rem;--navbar-padding-h: 1.5rem;--sidebar-width: 20rem;--sidebar-width-mobile: calc(var(--sidebar-width) * .82);--content-width: 740px;--homepage-width: 960px}.back-to-top{--back-to-top-color: var(--c-brand);--back-to-top-color-hover: var(--c-brand-light)}.DocSearch{--docsearch-primary-color: var(--c-brand);--docsearch-text-color: var(--c-text);--docsearch-highlight-color: var(--c-brand);--docsearch-muted-color: var(--c-text-quote);--docsearch-container-background: rgba(9, 10, 17, .8);--docsearch-modal-background: var(--c-bg-light);--docsearch-searchbox-background: var(--c-bg-lighter);--docsearch-searchbox-focus-background: var(--c-bg);--docsearch-searchbox-shadow: inset 0 0 0 2px var(--c-brand);--docsearch-hit-color: var(--c-text-light);--docsearch-hit-active-color: var(--c-bg);--docsearch-hit-background: var(--c-bg);--docsearch-hit-shadow: 0 1px 3px 0 var(--c-border-dark);--docsearch-footer-background: var(--c-bg)}.external-link-icon{--external-link-icon-color: var(--c-text-quote)}.medium-zoom-overlay{--medium-zoom-bg-color: var(--c-bg)}#nprogress{--nprogress-color: var(--c-brand)}.pwa-popup{--pwa-popup-text-color: var(--c-text);--pwa-popup-bg-color: var(--c-bg);--pwa-popup-border-color: var(--c-brand);--pwa-popup-shadow: 0 4px 16px var(--c-brand);--pwa-popup-btn-text-color: var(--c-bg);--pwa-popup-btn-bg-color: var(--c-brand);--pwa-popup-btn-hover-bg-color: var(--c-brand-light)}.search-box{--search-bg-color: var(--c-bg);--search-accent-color: var(--c-brand);--search-text-color: var(--c-text);--search-border-color: var(--c-border);--search-item-text-color: var(--c-text-lighter);--search-item-focus-bg-color: var(--c-bg-light)}html.dark{--c-brand: #3aa675;--c-brand-light: #349469;--c-bg: #22272e;--c-bg-light: #2b313a;--c-bg-lighter: #262c34;--c-bg-dark: #343b44;--c-bg-darker: #37404c;--c-text: #adbac7;--c-text-light: #96a7b7;--c-text-lighter: #8b9eb0;--c-text-lightest: #8094a8;--c-border: #3e4c5a;--c-border-dark: #34404c;--c-tip: #318a62;--c-warning: #e0ad15;--c-warning-bg: #2d2f2d;--c-warning-bg-light: #423e2a;--c-warning-bg-lighter: #44442f;--c-warning-border-dark: #957c35;--c-warning-details-bg: #39392d;--c-warning-title: #fdca31;--c-warning-text: #d8d96d;--c-warning-text-accent: #ffbf00;--c-warning-text-light: #ddb84b;--c-warning-text-quote: #ccab49;--c-danger: #fc1e38;--c-danger-bg: #39232c;--c-danger-bg-light: #4b2b35;--c-danger-bg-lighter: #553040;--c-danger-border-dark: #a25151;--c-danger-details-bg: #482936;--c-danger-title: #fc2d3b;--c-danger-text: #ea9ca0;--c-danger-text-accent: #fd3636;--c-danger-text-light: #d9777c;--c-danger-text-quote: #d56b6b;--c-details-bg: #323843;--c-badge-warning: var(--c-warning);--c-badge-warning-text: #3c2e05;--c-badge-danger: var(--c-danger);--c-badge-danger-text: #401416;--code-hl-bg-color: #363b46}html.dark .DocSearch{--docsearch-logo-color: var(--c-text);--docsearch-modal-shadow: inset 1px 1px 0 0 #2c2e40, 0 3px 8px 0 #000309;--docsearch-key-shadow: inset 0 -2px 0 0 #282d55, inset 0 0 1px 1px #51577d, 0 2px 2px 0 rgba(3, 4, 9, .3);--docsearch-key-gradient: linear-gradient(-225deg, #444950, #1c1e21);--docsearch-footer-shadow: inset 0 1px 0 0 rgba(73, 76, 106, .5), 0 -4px 8px 0 rgba(0, 0, 0, .2)}html,body{padding:0;margin:0;background-color:var(--c-bg);transition:background-color var(--t-color)}html.dark{color-scheme:dark}html{font-size:16px}body{font-family:var(--font-family);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:1rem;color:var(--c-text)}a{font-weight:500;color:var(--c-text-accent);text-decoration:none;overflow-wrap:break-word}p a code{font-weight:400;color:var(--c-text-accent)}kbd{font-family:var(--font-family-code);color:var(--c-text);background:var(--c-bg-lighter);border:solid .15rem var(--c-border-dark);border-bottom:solid .25rem var(--c-border-dark);border-radius:.15rem;padding:0 .15em}code{font-family:var(--font-family-code);color:var(--c-text-lighter);padding:.25rem .5rem;margin:0;font-size:.85em;background-color:var(--c-bg-light);border-radius:3px;overflow-wrap:break-word;transition:background-color var(--t-color)}blockquote{font-size:1rem;color:var(--c-text-quote);border-left:.2rem solid var(--c-border-dark);margin:1rem 0;padding:.25rem 0 .25rem 1rem;overflow-wrap:break-word}blockquote>p{margin:0}ul,ol{padding-left:1.2em}strong{font-weight:600}h1,h2,h3,h4,h5,h6{font-weight:600;line-height:1.25;overflow-wrap:break-word}h1:focus-visible,h2:focus-visible,h3:focus-visible,h4:focus-visible,h5:focus-visible,h6:focus-visible{outline:none}h1:hover .header-anchor,h2:hover .header-anchor,h3:hover .header-anchor,h4:hover .header-anchor,h5:hover .header-anchor,h6:hover .header-anchor{opacity:1}h1{font-size:2.2rem}h2{font-size:1.65rem;padding-bottom:.3rem;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color)}h3{font-size:1.35rem}h4{font-size:1.15rem}h5{font-size:1.05rem}h6{font-size:1rem}a.header-anchor{font-size:.85em;float:left;margin-left:-.87em;padding-right:.23em;margin-top:.125em;opacity:0;-webkit-user-select:none;-moz-user-select:none;user-select:none}@media print{a.header-anchor{display:none}}a.header-anchor:hover{text-decoration:none}a.header-anchor:focus-visible{opacity:1}@media print{a[href^="http://"]:after,a[href^="https://"]:after{content:" (" attr(href) ") "}}p,ul,ol{line-height:1.7;overflow-wrap:break-word}hr{border:0;border-top:1px solid var(--c-border)}table{border-collapse:collapse;margin:1rem 0;display:block;overflow-x:auto;transition:border-color var(--t-color)}tr{border-top:1px solid var(--c-border-dark);transition:border-color var(--t-color)}tr:nth-child(2n){background-color:var(--c-bg-light);transition:background-color var(--t-color)}tr:nth-child(2n) code{background-color:var(--c-bg-dark)}th,td{padding:.6em 1em;border:1px solid var(--c-border-dark);transition:border-color var(--t-color)}.arrow{display:inline-block;width:0;height:0}.arrow.up{border-left:4px solid transparent;border-right:4px solid transparent;border-bottom:6px solid var(--c-bg-arrow)}.arrow.down{border-left:4px solid transparent;border-right:4px solid transparent;border-top:6px solid var(--c-bg-arrow)}.arrow.right{border-top:4px solid transparent;border-bottom:4px solid transparent;border-left:6px solid var(--c-bg-arrow)}.arrow.left{border-top:4px solid transparent;border-bottom:4px solid transparent;border-right:6px solid var(--c-bg-arrow)}.badge{display:inline-block;font-size:14px;font-weight:600;height:18px;line-height:18px;border-radius:3px;padding:0 6px;color:var(--c-bg);vertical-align:top;transition:color var(--t-color),background-color var(--t-color)}.badge.tip{background-color:var(--c-badge-tip)}.badge.warning{background-color:var(--c-badge-warning);color:var(--c-badge-warning-text)}.badge.danger{background-color:var(--c-badge-danger);color:var(--c-badge-danger-text)}.badge+.badge{margin-left:5px}code[class*=language-],pre[class*=language-]{color:#ccc;background:none;font-family:var(--font-family-code);font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#2d2d2d}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.comment,.token.block-comment,.token.prolog,.token.doctype,.token.cdata{color:#999}.token.punctuation{color:#ccc}.token.tag,.token.attr-name,.token.namespace,.token.deleted{color:#ec5975}.token.function-name{color:#6196cc}.token.boolean,.token.number,.token.function{color:#f08d49}.token.property,.token.class-name,.token.constant,.token.symbol{color:#f8c555}.token.selector,.token.important,.token.atrule,.token.keyword,.token.builtin{color:#cc99cd}.token.string,.token.char,.token.attr-value,.token.regex,.token.variable{color:#7ec699}.token.operator,.token.entity,.token.url{color:#67cdcc}.token.important,.token.bold{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.token.inserted{color:#3eaf7c}.theme-default-content pre,.theme-default-content pre[class*=language-]{line-height:1.375;padding:1.3rem 1.5rem;margin:.85rem 0;border-radius:6px;overflow:auto}.theme-default-content pre code,.theme-default-content pre[class*=language-] code{color:#fff;padding:0;background-color:transparent!important;border-radius:0;overflow-wrap:unset;-webkit-font-smoothing:auto;-moz-osx-font-smoothing:auto}.theme-default-content .line-number{font-family:var(--font-family-code)}div[class*=language-]{position:relative;background-color:var(--code-bg-color);border-radius:6px}div[class*=language-]:before{content:attr(data-ext);position:absolute;z-index:3;top:.8em;right:1em;font-size:.75rem;color:var(--code-ln-color)}div[class*=language-] pre,div[class*=language-] pre[class*=language-]{background:transparent!important;position:relative;z-index:1}div[class*=language-] .highlight-lines{-webkit-user-select:none;-moz-user-select:none;user-select:none;padding-top:1.3rem;position:absolute;top:0;left:0;width:100%;line-height:1.375}div[class*=language-] .highlight-lines .highlight-line{background-color:var(--code-hl-bg-color)}div[class*=language-]:not(.line-numbers-mode) .line-numbers{display:none}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line{position:relative}div[class*=language-].line-numbers-mode .highlight-lines .highlight-line:before{content:" ";position:absolute;z-index:2;left:0;top:0;display:block;width:var(--code-ln-wrapper-width);height:100%}div[class*=language-].line-numbers-mode pre{margin-left:var(--code-ln-wrapper-width);padding-left:1rem;vertical-align:middle}div[class*=language-].line-numbers-mode .line-numbers{position:absolute;top:0;width:var(--code-ln-wrapper-width);text-align:center;color:var(--code-ln-color);padding-top:1.25rem;line-height:1.375;counter-reset:line-number}div[class*=language-].line-numbers-mode .line-numbers .line-number{position:relative;z-index:3;-webkit-user-select:none;-moz-user-select:none;user-select:none;height:1.375em}div[class*=language-].line-numbers-mode .line-numbers .line-number:before{counter-increment:line-number;content:counter(line-number);font-size:.85em}div[class*=language-].line-numbers-mode:after{content:"";position:absolute;top:0;left:0;width:var(--code-ln-wrapper-width);height:100%;border-radius:6px 0 0 6px;border-right:1px solid var(--code-hl-bg-color)}@media (max-width: 419px){.theme-default-content div[class*=language-]{margin:.85rem -1.5rem;border-radius:0}}.code-group__nav{margin-top:.85rem;margin-bottom:calc(-1.7rem - 6px);padding-bottom:calc(1.7rem - 6px);padding-left:10px;padding-top:10px;border-top-left-radius:6px;border-top-right-radius:6px;background-color:var(--code-bg-color)}.code-group__ul{margin:auto 0;padding-left:0;display:inline-flex;list-style:none}.code-group__nav-tab{border:0;padding:5px;cursor:pointer;background-color:transparent;font-size:.85em;line-height:1.4;color:#ffffffe6;font-weight:600}.code-group__nav-tab:focus{outline:none}.code-group__nav-tab:focus-visible{outline:1px solid rgba(255,255,255,.9)}.code-group__nav-tab-active{border-bottom:var(--c-brand) 1px solid}@media (max-width: 419px){.code-group__nav{margin-left:-1.5rem;margin-right:-1.5rem;border-radius:0}}.code-group-item{display:none}.code-group-item__active{display:block}.code-group-item>pre{background-color:orange}.custom-container{transition:color var(--t-color),border-color var(--t-color),background-color var(--t-color)}.custom-container .custom-container-title{font-weight:600}.custom-container .custom-container-title:not(:only-child){margin-bottom:-.4rem}.custom-container.tip,.custom-container.warning,.custom-container.danger{padding:.1rem 1.5rem;border-left-width:.5rem;border-left-style:solid;margin:1rem 0}.custom-container.tip{border-color:var(--c-tip);background-color:var(--c-tip-bg);color:var(--c-tip-text)}.custom-container.tip .custom-container-title{color:var(--c-tip-title)}.custom-container.tip a{color:var(--c-tip-text-accent)}.custom-container.tip code{background-color:var(--c-bg-dark)}.custom-container.warning{border-color:var(--c-warning);background-color:var(--c-warning-bg);color:var(--c-warning-text)}.custom-container.warning .custom-container-title{color:var(--c-warning-title)}.custom-container.warning a{color:var(--c-warning-text-accent)}.custom-container.warning blockquote{border-left-color:var(--c-warning-border-dark);color:var(--c-warning-text-quote)}.custom-container.warning code{color:var(--c-warning-text-light);background-color:var(--c-warning-bg-light)}.custom-container.warning details{background-color:var(--c-warning-details-bg)}.custom-container.warning details code{background-color:var(--c-warning-bg-lighter)}.custom-container.warning .external-link-icon{--external-link-icon-color: var(--c-warning-text-quote)}.custom-container.danger{border-color:var(--c-danger);background-color:var(--c-danger-bg);color:var(--c-danger-text)}.custom-container.danger .custom-container-title{color:var(--c-danger-title)}.custom-container.danger a{color:var(--c-danger-text-accent)}.custom-container.danger blockquote{border-left-color:var(--c-danger-border-dark);color:var(--c-danger-text-quote)}.custom-container.danger code{color:var(--c-danger-text-light);background-color:var(--c-danger-bg-light)}.custom-container.danger details{background-color:var(--c-danger-details-bg)}.custom-container.danger details code{background-color:var(--c-danger-bg-lighter)}.custom-container.danger .external-link-icon{--external-link-icon-color: var(--c-danger-text-quote)}.custom-container.details{display:block;position:relative;border-radius:2px;margin:1.6em 0;padding:1.6em;background-color:var(--c-details-bg)}.custom-container.details code{background-color:var(--c-bg-darker)}.custom-container.details h4{margin-top:0}.custom-container.details figure:last-child,.custom-container.details p:last-child{margin-bottom:0;padding-bottom:0}.custom-container.details summary{outline:none;cursor:pointer}.home{padding:var(--navbar-height) 2rem 0;max-width:var(--homepage-width);margin:0 auto;display:block}.home .hero{text-align:center}.home .hero img{max-width:100%;max-height:280px;display:block;margin:3rem auto 1.5rem}.home .hero h1{font-size:3rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.8rem auto}.home .hero .actions{display:flex;flex-wrap:wrap;gap:1rem;justify-content:center}.home .hero .description{max-width:35rem;font-size:1.6rem;line-height:1.3;color:var(--c-text-lightest)}.home .hero .action-button{display:inline-block;font-size:1.2rem;padding:.8rem 1.6rem;border-width:2px;border-style:solid;border-radius:4px;transition:background-color var(--t-color);box-sizing:border-box}.home .hero .action-button.primary{color:var(--c-bg);background-color:var(--c-brand);border-color:var(--c-brand)}.home .hero .action-button.primary:hover{background-color:var(--c-brand-light)}.home .hero .action-button.secondary{color:var(--c-brand);background-color:var(--c-bg);border-color:var(--c-brand)}.home .hero .action-button.secondary:hover{color:var(--c-bg);background-color:var(--c-brand-light)}.home .features{border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding:1.2rem 0;margin-top:2.5rem;display:flex;flex-wrap:wrap;align-items:flex-start;align-content:stretch;justify-content:space-between}.home .feature{flex-grow:1;flex-basis:30%;max-width:30%}.home .feature h2{font-size:1.4rem;font-weight:500;border-bottom:none;padding-bottom:0;color:var(--c-text-light)}.home .feature p{color:var(--c-text-lighter)}.home .theme-default-content{padding:0;margin:0}.home .footer{padding:2.5rem;border-top:1px solid var(--c-border);text-align:center;color:var(--c-text-lighter);transition:border-color var(--t-color)}@media (max-width: 719px){.home .features{flex-direction:column}.home .feature{max-width:100%;padding:0 2.5rem}}@media (max-width: 419px){.home{padding-left:1.5rem;padding-right:1.5rem}.home .hero img{max-height:210px;margin:2rem auto 1.2rem}.home .hero h1{font-size:2rem}.home .hero h1,.home .hero .description,.home .hero .actions{margin:1.2rem auto}.home .hero .description{font-size:1.2rem}.home .hero .action-button{font-size:1rem;padding:.6rem 1.2rem}.home .feature h2{font-size:1.25rem}}.page{padding-top:var(--navbar-height);padding-left:var(--sidebar-width)}.navbar{position:fixed;z-index:20;top:0;left:0;right:0;height:var(--navbar-height);box-sizing:border-box;border-bottom:1px solid var(--c-border);background-color:var(--c-bg-navbar);transition:background-color var(--t-color),border-color var(--t-color)}.sidebar{font-size:16px;width:var(--sidebar-width);position:fixed;z-index:10;margin:0;top:var(--navbar-height);left:0;bottom:0;box-sizing:border-box;border-right:1px solid var(--c-border);overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--c-brand) var(--c-border);background-color:var(--c-bg-sidebar);transition:transform var(--t-transform),background-color var(--t-color),border-color var(--t-color)}.sidebar::-webkit-scrollbar{width:7px}.sidebar::-webkit-scrollbar-track{background-color:var(--c-border)}.sidebar::-webkit-scrollbar-thumb{background-color:var(--c-brand)}.sidebar-mask{position:fixed;z-index:9;top:0;left:0;width:100vw;height:100vh;display:none}.theme-container.sidebar-open .sidebar-mask{display:block}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1){transform:rotate(45deg) translate3d(5.5px,5.5px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(2){transform:scale3d(0,1,1)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform:rotate(-45deg) translate3d(6px,-6px,0)}.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(1),.theme-container.sidebar-open .navbar>.toggle-sidebar-button .icon span:nth-child(3){transform-origin:center}.theme-container.no-navbar .theme-default-content h1,.theme-container.no-navbar .theme-default-content h2,.theme-container.no-navbar .theme-default-content h3,.theme-container.no-navbar .theme-default-content h4,.theme-container.no-navbar .theme-default-content h5,.theme-container.no-navbar .theme-default-content h6{margin-top:1.5rem;padding-top:0}.theme-container.no-navbar .page{padding-top:0}.theme-container.no-navbar .sidebar{top:0}.theme-container.no-sidebar .sidebar{display:none}@media (max-width: 719px){.theme-container.no-sidebar .sidebar{display:block}}.theme-container.no-sidebar .page{padding-left:0}.theme-default-content a:hover{text-decoration:underline}.theme-default-content img{max-width:100%}.theme-default-content h1,.theme-default-content h2,.theme-default-content h3,.theme-default-content h4,.theme-default-content h5,.theme-default-content h6{margin-top:calc(.5rem - var(--navbar-height));padding-top:calc(1rem + var(--navbar-height));margin-bottom:0}.theme-default-content h1:first-child,.theme-default-content h2:first-child,.theme-default-content h3:first-child,.theme-default-content h4:first-child,.theme-default-content h5:first-child,.theme-default-content h6:first-child{margin-bottom:1rem}.theme-default-content h1:first-child+p,.theme-default-content h1:first-child+pre,.theme-default-content h1:first-child+.custom-container,.theme-default-content h2:first-child+p,.theme-default-content h2:first-child+pre,.theme-default-content h2:first-child+.custom-container,.theme-default-content h3:first-child+p,.theme-default-content h3:first-child+pre,.theme-default-content h3:first-child+.custom-container,.theme-default-content h4:first-child+p,.theme-default-content h4:first-child+pre,.theme-default-content h4:first-child+.custom-container,.theme-default-content h5:first-child+p,.theme-default-content h5:first-child+pre,.theme-default-content h5:first-child+.custom-container,.theme-default-content h6:first-child+p,.theme-default-content h6:first-child+pre,.theme-default-content h6:first-child+.custom-container{margin-top:2rem}@media (max-width: 959px){.sidebar{font-size:15px;width:var(--sidebar-width-mobile)}.page{padding-left:var(--sidebar-width-mobile)}}@media (max-width: 719px){.sidebar{top:0;padding-top:var(--navbar-height);transform:translate(-100%)}.page{padding-left:0}.theme-container.sidebar-open .sidebar{transform:translate(0)}.theme-container.no-navbar .sidebar{padding-top:0}}@media (max-width: 419px){h1{font-size:1.9rem}}.navbar{--navbar-line-height: calc( var(--navbar-height) - 2 * var(--navbar-padding-v) );padding:var(--navbar-padding-v) var(--navbar-padding-h);line-height:var(--navbar-line-height)}.navbar .logo{height:var(--navbar-line-height);margin-right:var(--navbar-padding-v);vertical-align:top}.navbar .site-name{font-size:1.3rem;font-weight:600;color:var(--c-text);position:relative}.navbar .navbar-items-wrapper{display:flex;position:absolute;box-sizing:border-box;top:var(--navbar-padding-v);right:var(--navbar-padding-h);height:var(--navbar-line-height);padding-left:var(--navbar-padding-h);white-space:nowrap;font-size:.9rem}.navbar .navbar-items-wrapper .search-box{flex:0 0 auto;vertical-align:top}@media screen and (max-width: 719px){.navbar{padding-left:4rem}.navbar .site-name{display:block;width:calc(100vw - 11rem);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.navbar .can-hide{display:none}}.navbar-items{display:inline-block}@media print{.navbar-items{display:none}}.navbar-items a{display:inline-block;line-height:1.4rem;color:inherit}.navbar-items a:hover,.navbar-items a.router-link-active{color:var(--c-text)}.navbar-items .navbar-item{position:relative;display:inline-block;margin-left:1.5rem;line-height:var(--navbar-line-height)}.navbar-items .navbar-item:first-child{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.router-link-active{margin-bottom:-2px;border-bottom:2px solid var(--c-text-accent)}@media (max-width: 719px){.navbar-items .navbar-item{margin-left:0}.navbar-items .navbar-item>a:hover,.navbar-items .navbar-item>a.router-link-active{margin-bottom:0;border-bottom:none}.navbar-items a:hover,.navbar-items a.router-link-active{color:var(--c-text-accent)}}.toggle-sidebar-button{position:absolute;top:.6rem;left:1rem;display:none;padding:.6rem;cursor:pointer}.toggle-sidebar-button .icon{display:flex;flex-direction:column;justify-content:center;align-items:center;width:1.25rem;height:1.25rem;cursor:inherit}.toggle-sidebar-button .icon span{display:inline-block;width:100%;height:2px;border-radius:2px;background-color:var(--c-text);transition:transform var(--t-transform)}.toggle-sidebar-button .icon span:nth-child(2){margin:6px 0}@media screen and (max-width: 719px){.toggle-sidebar-button{display:block}}.toggle-color-mode-button{display:flex;margin:auto;margin-left:1rem;border:0;background:none;color:var(--c-text);opacity:.8;cursor:pointer}@media print{.toggle-color-mode-button{display:none}}.toggle-color-mode-button:hover{opacity:1}.toggle-color-mode-button .icon{width:1.25rem;height:1.25rem}.DocSearch{transition:background-color var(--t-color)}.navbar-dropdown-wrapper{cursor:pointer}.navbar-dropdown-wrapper .navbar-dropdown-title,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:block;font-size:.9rem;font-family:inherit;cursor:inherit;padding:inherit;line-height:1.4rem;background:transparent;border:none;font-weight:500;color:var(--c-text)}.navbar-dropdown-wrapper .navbar-dropdown-title:hover,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{border-color:transparent}.navbar-dropdown-wrapper .navbar-dropdown-title .arrow,.navbar-dropdown-wrapper .navbar-dropdown-title-mobile .arrow{vertical-align:middle;margin-top:-1px;margin-left:.4rem}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile{display:none;font-weight:600;font-size:inherit}.navbar-dropdown-wrapper .navbar-dropdown-title-mobile:hover{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item{color:inherit;line-height:1.7rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{margin:.45rem 0 0;border-top:1px solid var(--c-border);padding:1rem 0 .45rem;font-size:.9rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>span{padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a{font-weight:inherit}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle>a.router-link-active:after{display:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper{padding:0;list-style:none}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem-wrapper .navbar-dropdown-subitem{font-size:.9em}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a{display:block;line-height:1.7rem;position:relative;border-bottom:none;font-weight:400;margin-bottom:0;padding:0 1.5rem 0 1.25rem}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a:hover,.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.router-link-active{color:var(--c-text-accent)}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.router-link-active:after{content:"";width:0;height:0;border-left:5px solid var(--c-text-accent);border-top:3px solid transparent;border-bottom:3px solid transparent;position:absolute;top:calc(50% - 2px);left:9px}.navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item:first-child .navbar-dropdown-subtitle{margin-top:0;padding-top:0;border-top:0}.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile.open .navbar-dropdown-title-mobile{margin-bottom:.5rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title,.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:none}.navbar-dropdown-wrapper.mobile .navbar-dropdown-title-mobile{display:block}.navbar-dropdown-wrapper.mobile .navbar-dropdown{transition:height .1s ease-out;overflow:hidden}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle{border-top:0;margin-top:0;padding-top:0;padding-bottom:0}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subtitle,.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item>a{font-size:15px;line-height:2rem}.navbar-dropdown-wrapper.mobile .navbar-dropdown .navbar-dropdown-item .navbar-dropdown-subitem{font-size:14px;padding-left:1rem}.navbar-dropdown-wrapper:not(.mobile){height:1.8rem}.navbar-dropdown-wrapper:not(.mobile):hover .navbar-dropdown,.navbar-dropdown-wrapper:not(.mobile).open .navbar-dropdown{display:block!important}.navbar-dropdown-wrapper:not(.mobile).open:blur{display:none}.navbar-dropdown-wrapper:not(.mobile) .navbar-dropdown{display:none;height:auto!important;box-sizing:border-box;max-height:calc(100vh - 2.7rem);overflow-y:auto;position:absolute;top:100%;right:0;background-color:var(--c-bg-navbar);padding:.6rem 0;border:1px solid var(--c-border);border-bottom-color:var(--c-border-dark);text-align:left;border-radius:.25rem;white-space:nowrap;margin:0}.page{padding-bottom:2rem;display:block}.page .theme-default-content{max-width:var(--content-width);margin:0 auto;padding:2rem 2.5rem;padding-top:0}@media (max-width: 959px){.page .theme-default-content{padding:2rem}}@media (max-width: 419px){.page .theme-default-content{padding:1.5rem}}.page-meta{max-width:var(--content-width);margin:0 auto;padding:1rem 2.5rem;overflow:auto}@media (max-width: 959px){.page-meta{padding:2rem}}@media (max-width: 419px){.page-meta{padding:1.5rem}}.page-meta .meta-item{cursor:default;margin-top:.8rem}.page-meta .meta-item .meta-item-label{font-weight:500;color:var(--c-text-lighter)}.page-meta .meta-item .meta-item-info{font-weight:400;color:var(--c-text-quote)}.page-meta .edit-link{display:inline-block;margin-right:.25rem}@media print{.page-meta .edit-link{display:none}}.page-meta .last-updated{float:right}@media (max-width: 719px){.page-meta .last-updated{font-size:.8em;float:none}.page-meta .contributors{font-size:.8em}}.page-nav{max-width:var(--content-width);margin:0 auto;padding:1rem 2.5rem 2rem;padding-bottom:0}@media (max-width: 959px){.page-nav{padding:2rem}}@media (max-width: 419px){.page-nav{padding:1.5rem}}.page-nav .inner{min-height:2rem;margin-top:0;border-top:1px solid var(--c-border);transition:border-color var(--t-color);padding-top:1rem;overflow:auto}.page-nav .prev a:before{content:"←"}.page-nav .next{float:right}.page-nav .next a:after{content:"→"}.sidebar ul{padding:0;margin:0;list-style-type:none}.sidebar a{display:inline-block}.sidebar .navbar-items{display:none;border-bottom:1px solid var(--c-border);transition:border-color var(--t-color);padding:.5rem 0 .75rem}.sidebar .navbar-items a{font-weight:600}.sidebar .navbar-items .navbar-item{display:block;line-height:1.25rem;font-size:1.1em;padding:.5rem 0 .5rem 1.5rem}.sidebar .sidebar-items{padding:1.5rem 0}@media (max-width: 719px){.sidebar .navbar-items{display:block}.sidebar .navbar-items .navbar-dropdown-wrapper .navbar-dropdown .navbar-dropdown-item a.router-link-active:after{top:calc(1rem - 2px)}.sidebar .sidebar-items{padding:1rem 0}}.sidebar-item{cursor:default;border-left:.25rem solid transparent;color:var(--c-text)}.sidebar-item:focus-visible{outline-width:1px;outline-offset:-1px}.sidebar-item.active:not(p.sidebar-heading){font-weight:600;color:var(--c-text-accent);border-left-color:var(--c-text-accent)}.sidebar-item.sidebar-heading{transition:color .15s ease;font-size:1.1em;font-weight:700;padding:.35rem 1.5rem .35rem 1.25rem;width:100%;box-sizing:border-box;margin:0}.sidebar-item.sidebar-heading+.sidebar-item-children{transition:height .1s ease-out;overflow:hidden;margin-bottom:.75rem}.sidebar-item.collapsible{cursor:pointer}.sidebar-item.collapsible .arrow{position:relative;top:-.12em;left:.5em}.sidebar-item:not(.sidebar-heading){font-size:1em;font-weight:400;display:inline-block;margin:0;padding:.35rem 1rem .35rem 2rem;line-height:1.4;width:100%;box-sizing:border-box}.sidebar-item:not(.sidebar-heading)+.sidebar-item-children{padding-left:1rem;font-size:.95em}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading){padding:.25rem 1rem .25rem 1.75rem}.sidebar-item-children .sidebar-item-children .sidebar-item:not(.sidebar-heading).active{font-weight:500;border-left-color:transparent}a.sidebar-heading+.sidebar-item-children .sidebar-item:not(.sidebar-heading).active{border-left-color:transparent}a.sidebar-item{cursor:pointer}a.sidebar-item:hover{color:var(--c-text-accent)}.table-of-contents .badge{vertical-align:middle}.dropdown-enter-from,.dropdown-leave-to{height:0!important}.fade-slide-y-enter-active{transition:all .2s ease}.fade-slide-y-leave-active{transition:all .2s cubic-bezier(1,.5,.8,1)}.fade-slide-y-enter-from,.fade-slide-y-leave-to{transform:translateY(10px);opacity:0}@font-face{font-family:CabinetGrotesk-Variable;src:url(/fonts/CabinetGrotesk-Variable.ttf);font-weight:900}@font-face{font-family:ui-sans-serif;src:url(/fonts/IBMPlexSans-Regular.ttf);font-weight:400}@font-face{font-family:ui-sans-serif;src:url(/fonts/IBMPlexSans-Bold.ttf);font-weight:700}@font-face{font-family:NewSpirit;src:url(/fonts/NewSpirit.otf);font-weight:900}.sidebar-item{background-color:var(--primary-dark);color:var(--secondary-dark);width:90%!important;position:relative}.sidebar-item span.arrow{top:.4em!important;float:right!important;border:none!important;height:20px;width:20px;background-repeat:no-repeat!important;background-size:contain!important}.sidebar-item span.arrow.right{background-image:url(../assets/sidebar-plus.svg)!important}.sidebar-item span.arrow.down{background-image:url(../assets/sidebar-minus.svg)!important}.sidebar-item:hover{background-color:var(--primary-light)!important;color:var(--secondary-dark)!important;border-radius:8px}.sidebar-item.active{background-color:var(--primary-dark)!important;color:var(--secondary-light)!important;font-weight:bolder!important;border:none!important}.sidebar-item.active:hover,.sidebar-item.active:nth-last-child(1){border-radius:8px;background-color:var(--primary-light)!important}ul.sidebar-items>li{margin-top:1rem}ul.sidebar-items>li:nth-child(2)>.sidebar-item:before,ul.sidebar-items>li:nth-child(3)>.sidebar-item:before{content:"";position:absolute;top:-8px;left:5%;width:90%;height:1px;background-color:var(--primary-bright)}.homepage{background-color:var(--c-bg-dark)}.homepage:before{content:"";position:fixed;top:0%;left:-20%;width:140%;height:100vh;z-index:0;background:url(/assets/homepage.svg) center/300px 150px repeat}.homepage>.navbar{background-color:var(--c-bg-dark)}.homepage>.page{position:relative;padding:0!important;height:initial;min-height:100vh}.homepage>.page>.theme-default-content{max-width:100%;padding:0 1rem}.homepage_content{display:flex;align-items:center;width:100%;min-height:100vh;padding:calc(var(--navbar-height) + 2rem) 0 2rem;box-sizing:border-box}.homepage_container{display:flex;flex-wrap:wrap;justify-content:center;margin:0 auto;gap:4rem}.homepage_item{display:flex;flex-direction:column;justify-content:space-between;text-align:left;width:calc(33.3333333333% - 8rem);background-color:var(--c-bg-light);border:none;padding:40px 25px;min-height:280px;box-shadow:0 2px 4px 0 var(--c-glow);transition:box-shadow .3s ease-in-out,background-color .3s ease-in-out;text-decoration:none!important}.homepage_item:hover{background-color:var(--c-bg-darker);box-shadow:0 2px 30px 0 var(--c-glow)}.homepage_item h2{padding:0;margin:0!important;color:var(--c-text);font-family:var(--font-family-title);font-size:1.75rem;line-height:3rem;font-weight:900;border:none}.homepage_item p{padding:0;margin:20px 0 0!important;color:var(--c-text-lighter);font-size:1.125rem;line-height:1.5;font-weight:500;border:none}.homepage_title{display:flex;align-items:center;justify-content:space-between;width:100%}.homepage footer{display:none!important}:root{--c-brand: #f9ff73;--c-brand-light: #faff81;--c-bg: #faf8f5;--c-bg-dark: #faf8f5;--c-bg-light: #ffffff;--c-bg-lighter: #fafafa;--c-bg-navbar: var(--c-bg);--c-bg-sidebar: var(--c-bg);--c-bg-arrow: #f9ff73;--c-glow: rgba(0, 0, 0, .2);--c-text: #010101;--c-text-accent: var(--c-brand);--c-text-light: #6a7380;--c-text-lighter: #6a7380;--c-text-lightest: rgb(120 220 232);--c-text-quote: #f9ff73;--c-border: #edebe9;--c-border-dark: #e1dfdd;--c-tip: rgb(120, 220, 232);--c-tip-bg: rgba(120, 220, 232, .3);--c-tip-title: var(--c-tip);--c-tip-text: var(--c-text);--c-tip-text-accent: var(--c-text-accent);--c-warning: rgb(249, 255, 115);--c-warning-bg: rgba(249, 255, 115, .3);--c-warning-title: var(--c-warning);--c-warning-text: var(--c-text);--c-warning-text-accent: var(--c-text-accent);--c-danger: #e46b71;--c-danger-bg: rgba(228, 107, 113, .3);--c-danger-title: var(--c-danger);--c-danger-text: var(--c-text);--c-details-bg: var(--c-bg-light);--c-danger-text-accent: var(--c-text);--c-badge-tip: var(--c-tip);--c-badge-warning: var(--c-warning);--c-badge-danger: var(--c-danger);--t-color: .3s ease;--t-transform: .3s ease;--code-bg-color: #282c34;--code-hl-bg-color: rgba(0, 0, 0, .66);--code-ln-color: #9e9e9e;--code-ln-wrapper-width: 3.5rem;--navbar-height: 3.6rem;--navbar-padding-v: .8rem;--navbar-line-height: calc(var(--navbar-height) - 2 * var(--navbar-padding-v));--homepage-width: 1275px;--content-width: 1275px;--search-input-width: 10rem !important;--search-result-width: 20rem !important;--font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;--font-family-title: NewSpirit, Inter, CabinetGrotesk-Variable, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans, sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol, Noto Color Emoji;--primary-dark: #06080f;--primary-light: #1e212b;--primary-bright: #313442;--secondary-dark: #9d9e91;--secondary-light: #f0f2da}html.dark{--c-brand: #f9ff73;--c-brand-light: #faff81;--c-bg-arrow: #f9ff73;--c-text: #f0f2da;--c-text-light: #9d9e91;--c-text-lighter: #9d9e91;--c-text-lightest: rgb(120 220 232);--c-bg: var(--primary-dark);--c-bg-dark: #141721;--c-bg-darker: #06080f;--c-bg-light: #1f212b;--c-bg-lighter: #2b2e30;--c-border: #2b2e30;--c-glow: rgba(20, 23, 33, .8);--c-tip: rgb(120, 220, 232);--c-tip-bg: rgba(120, 220, 232, .3);--c-tip-title: var(--c-tip);--c-tip-text: var(--c-text);--c-tip-text-accent: var(--c-text-accent);--c-warning: rgb(249, 255, 115);--c-warning-bg: rgba(249, 255, 115, .3);--c-warning-title: var(--c-warning);--c-warning-text: var(--c-text);--c-warning-text-accent: var(--c-text-accent);--c-danger: #e46b71;--c-danger-bg: rgba(228, 107, 113, .3);--c-danger-title: var(--c-danger);--c-danger-text: var(--c-text);--c-details-bg: var(--c-bg-light);--c-danger-text-accent: var(--c-text);--c-badge-tip: var(--c-tip);--c-badge-warning: var(--c-warning);--c-badge-danger: var(--c-danger)}.navbar-items .navbar-item{margin-left:1rem}.search-box{order:-1;margin:0!important;margin-right:1rem!important;--search-item-focus-bg-color: var(--c-bg-light)}.search-box input{--search-border-color: var(--c-text)}.site-name{display:none!important}.navbar .logo{height:24px;vertical-align:middle}.split_content{display:flex;flex-wrap:wrap;width:100%;gap:3rem}.split_side{width:calc(50% - 1.5rem)}@media (max-width: 1140px){.homepage_item{width:calc(50% - 8rem)}.split_side{width:100%}nav{display:none!important}}@media (max-width: 720px){.homepage_container{width:100%;margin:0 auto}.homepage_item{width:100%;min-height:200px}.search-box{margin-right:0rem!important}}h1,h2,h3{font-family:var(--font-family-title)!important;font-weight:900!important;padding-bottom:16px}h2,h3{margin-top:1rem!important}div.divider{margin:3rem auto;border-bottom:1px solid var(--primary-bright)}div.request-url{color:var(--c-text);font-size:1.15rem;font-weight:300;line-height:1.25;overflow-wrap:break-word;background:#121415;padding:15px;border:1px solid rgb(87,85,85);border-radius:10px}:root{--search-bg-color: #ffffff;--search-accent-color: #3eaf7c;--search-text-color: #2c3e50;--search-border-color: #eaecef;--search-item-text-color: #5d81a5;--search-item-focus-bg-color: #f3f4f5;--search-input-width: 8rem;--search-result-width: 20rem}.search-box{display:inline-block;position:relative;margin-left:1rem}@media print{.search-box{display:none}}.search-box input{-webkit-appearance:none;-moz-appearance:none;appearance:none;cursor:text;width:var(--search-input-width);height:2rem;color:var(--search-text-color);display:inline-block;border:1px solid var(--search-border-color);border-radius:2rem;font-size:.9rem;line-height:2rem;padding:0 .5rem 0 2rem;outline:none;transition:all ease .3s;background:var(--search-bg-color) url(/assets/search-0782d0d1.svg) .6rem .5rem no-repeat;background-size:1rem}.search-box input:focus{cursor:auto;border-color:var(--search-accent-color)}.search-box .suggestions{background:var(--search-bg-color);width:var(--search-result-width);position:absolute;top:2rem;right:0;border:1px solid var(--search-border-color);border-radius:6px;padding:.4rem;list-style-type:none}.search-box .suggestion{line-height:1.4;padding:.4rem .6rem;border-radius:4px;cursor:pointer}.search-box .suggestion.focus{background-color:var(--search-item-focus-bg-color)}.search-box .suggestion.focus a{color:var(--search-accent-color)}.search-box .suggestion a{white-space:normal;color:var(--search-item-text-color)}.search-box .suggestion .page-title{font-weight:600}.search-box .suggestion .page-header{font-size:.9em;margin-left:.25em}@media (max-width: 719px){.search-box input{cursor:pointer;width:0;border-color:transparent;position:relative}.search-box input:focus{cursor:text;left:0;width:10rem}}@media (max-width: 419px){.search-box input:focus{width:8rem}.search-box .suggestions{width:calc(100vw - 4rem);right:-.5rem}}.code-div[data-v-c21333f5]{font-size:1.15rem;font-weight:300;line-height:1.25;overflow-wrap:break-word;background:#121415;padding:15px;border:1px solid rgb(87,85,85);border-radius:10px;display:flex;justify-content:space-between;align-items:center}.copyButton[data-v-c21333f5]{background-color:var(--c-brand);border:none;color:#000;text-align:center;text-decoration:none;display:inline-block;font-size:16px;margin:4px 2px;cursor:pointer;border-radius:15px;padding:8px 15px} diff --git a/build/1-apillon-api.html b/build/1-apillon-api.html new file mode 100644 index 00000000..b1e1f6de --- /dev/null +++ b/build/1-apillon-api.html @@ -0,0 +1,76 @@ + + + + + + + + +Apillon API | Apillon Wiki + + + + ++ + + diff --git a/build/10-flutter-sdk.html b/build/10-flutter-sdk.html new file mode 100644 index 00000000..6b0c4f03 --- /dev/null +++ b/build/10-flutter-sdk.html @@ -0,0 +1,207 @@ + + + + + + + + +Apillon Wiki Apillon API
Endpoints
The list of endpoints where API is available:
Environment URL Production https://api.apillon.io Testing Coming soon... API to Web3
Apillon API is a set of RESTful API endpoints allowing developers to integrate Apillon modules into their Web3 applications.
Unless clearly marked as public, all routes are private and require an API key.
Requests
The server speaks JSON. It is recommended that every call to the server includes a
Content-Type
header set toapplication/json;
.Authentication and authorization
API routes restrict public access and require authentication.
Requests must include a basic auth HTTP header field in the form of
Authorization: Basic <credentials>
, where credentials represent the Base64 encoding of API key and API key secret joined by a single colon:
.API keys could be generated on the developer dashboard under Project settings.
Authentication errors
Every request goes through authentication middleware, where the following errors can occur:
Status Message Description 400 Missing Authorization header Request is missing authorization header. 400 Malformed Authorization header Authorization header field has an invalid form. 401 Invalid API key or API key secret Authorization header is valid, but credentials in it are not. Authorization errors
Each endpoint requires a certain role or permission from the API key.
There are three types of permissions that could be assigned to an API key:
Code Name Description 50 KEY_EXECUTE Permission to execute certain actions 51 KEY_WRITE Permission to create, modify or delete records 52 KEY_READ Permission to read record These permissions could be assigned to an API key for every attached service (e.g., Web3 Storage (Crust), Web3 Authentication (KILT), etc.).
If a request is made with an API key that lacks permission for a called endpoint, the following errors can occur:
Status Message Description 403 Insufficient permissions - missing permission name
permissionAPI key lacks required permission for called service. 403 Insufficient permissions to access this record API key has required permissions for endpoint, but it does not have the right to access the addressed record (i.e., a record belongs to a different project). Listing requests
Endpoints starting with "List" are intended to list different data, where the response contains the below properties.
Name Description items Records on a specified page that match the current query total Number of all records that match the query limit Number of items on a page (default: 20). page Current page Listing endpoints by default supports the query parameters below:
Name Description Required search Search the items usually by name or some other property specifying this item. false page Items are paginated by default. This parameter is used to get items from a specific page. false limit Number of items on a page (default: 20). false orderBy One or multiple properties, separated by a comma, used to order data. false desc Boolean
values, mapped to the index of theorderBy
parameter. Defaults to false.false status Integer values, to filter by the entity's status (each entity has corresponding status codes) false Responses
Every response has a unique ID, which helps identify potential issues. It also includes a status code that can help identify the cause of a potential problem.
Query requests through the
GET
method can return status codes200
,400
,401
,403
, or500
. Mutations throughPOST
,PUT
, andDELETE
can also return codes201
and422
. Invalid routes return status code404
.A successful request includes a
data
key, which holds a valid response object.{ + "id": ..., + "status": ..., + "data": { ... }, +} +
List of responses:
- 200: Success
- 201: Creation successful
- 400: Bad request
- 401: Unauthenticated access
- 402: Payment required
- 403: Unauthorized access
- 404: Path not found
- 422: Data validation failed
- 500: System error
Error handling
A request fails if response code is not 200 or 201. The Apillon API returns two types of errors.
Code exception
Errors include a unique code number, a property that caused the error, and an error message. The code number helps identify potential issues and points to their exact position in the system.
Fields in code exception:
Field Description id Unique ID of request code Apillon API internal error code pointing to the exact position in the system message Message describing the error path Endpoint that threw the error timestamp Date when the error occurred { + "id": "c46821e7-a6c3-4377-bc32-0001e348ceb4", + "code": 40406005, + "message": "FILE_DOES_NOT_EXIST", + "path": "/storage/cee9f151-a371-495b-acd2-4362fbb87780/file/xxx/detail", + "timestamp": "2022-12-29T11:54:47.555Z" +} +
Validation exception
Unprocessable entity
422 Error status
includes anerrors
key containing a list of error objects.This error typically occurs when the request body is not valid (i.e., it is invalid or missing keys).
Errors include a unique code number, a property that caused the error, and an error message. The code number helps identify potential issues and points to their exact position in the system.
{ + ... + "errors": [ + { + "code": 42200008, + "property": "fileName", + "message": "FILE_NAME_NOT_PRESENT" + }, + ] +} +
Fields in validation exception:
Field Description id Unique ID of request model Apillon API model used to validate request payload errors Array of errors path Endpoint that threw the error timestamp Date when the error occurred Common errors
Through the whole Apillon API, the same errors can occur. The reason behind it can be current subscription package limits or current credit balance.
Not enough storage space
One of the limits based on the project subscription package is available storage space (on the IPFS node). If a project reaches the storage space limit, the following error will occur.
{ + ... + "code": 40006003, + "message": "NOT_ENOUGH_STORAGE_SPACE", + ... +} +
Credit balance too low
Some nonrecurrent actions require payment with credits. If a project's credit balance is lower than price of executed action, API will return status 402 and the following response.
{ + ... + "code": 40210000, + "message": "CREDIT_BALANCE_TOO_LOW", + ... +} +
Project
Api key is created inside a project and can be used to get project details through Apillon API.
Credit balance
API to get project credit balance
GET /project/creditResponse fields
Name Type Description balance number
Current credit balance - amount of credits in project, that can be used to perform different actions curl --location --request GET "https://api.apillon.io/project/credit" \ +--header "Authorization: Basic :credentials" +
{ + "id": "ec700ddd-4a0d-4d6d-b3ba-64b7ab031c4b", + "status": 200, + "data": { + "balance": 120 + } +} +
API Code Examples
Examples for using the Apillon API in PHP, .NET (C#) and Python can be found on our code examples github repo
Apillon Flutter SDK | Apillon Wiki + + + + ++ + + diff --git a/build/2-storage-api.html b/build/2-storage-api.html new file mode 100644 index 00000000..4ced450d --- /dev/null +++ b/build/2-storage-api.html @@ -0,0 +1,376 @@ + + + + + + + + +Apillon Wiki Apillon Flutter SDK
This package provides Dart and Flutter developers with tools and libraries to interact with Apillon services, simplifying the use of Apillon's REST API by reducing boilerplate code and streamlining multi-step processes into single operations.
Requirements
- Dart SDK: '>=3.2.2 <4.0.0'
- An Apillon API key and secret
- http package version 1.1.0
Getting started
To use the Apillon Flutter SDK, you must first register an account at Apillon.io, create a project, and generate an API key with the appropriate permissions.
The Flutter SDK package is available as a Dart package on pub.dev and you can also check it out directly on GitHub. To include it in your project, add the following to your
pubspec.yaml
file:dependencies: + apillon_flutter: ^0.0.1 +
Initialization
To start using the SDK, you need to import it and configure it with your Apillon API key and secret. Here's an example of how to initialize the Storage module:
import 'package:apillon_flutter/apillon_flutter.dart'; + +void main() { + var storage = Storage(ApillonConfig( + key: 'yourApiKey', + secret: 'yourApiSecret', + )); +} +
All modules in the Apillon Flutter SDK require the same initial configuration of
key
andsecret
.Modules
The Apillon Flutter SDK consists of several modules, each corresponding to a specific Apillon service. Below are examples of how to use some of these modules.
Storage
The Storage module provides functionalities for interacting with the Storage service.
Usage example
import 'dart:io'; +import 'package:apillon_flutter/apillon_flutter.dart'; +import 'package:path/path.dart' as path; + +void main() async { + var storage = Storage(ApillonConfig( + key: 'yourApiKey', + secret: 'yourApiSecret', + )); + + // List all buckets + var buckets = await storage.listBuckets(IApillonPagination()); + print('Buckets:'); + for (var bucket in buckets) { + print('${bucket.name} - ${bucket.uuid}'); + } + + var bucketUuid = 'eaff2672-3012-46fb-9278-5efacc6cb616'; + + // Get specific bucket details + var bucketDetails = await storage.bucket(bucketUuid).get(); + print('Bucket Details: ${bucketDetails.name}, Size: ${bucketDetails.size}'); + + // List files in the bucket + var files = await storage.bucket(bucketUuid).listFiles(IBucketFilesRequest()); + print('Files in bucket:'); + for (var file in files) { + print('${file.name} - ${file.uuid}'); + } + + // Upload files from a folder + var uploadDir = path.join(Directory.current.path, 'my-folder'); + print('Uploading files from $uploadDir'); + await storage.bucket(bucketUuid).uploadFromFolder(uploadDir, IFileUploadRequest()); + + // Upload a single file from buffer + var filePath = path.join(Directory.current.path, 'file.txt'); + var fileBytes = File(filePath).readAsBytesSync(); + await storage.bucket(bucketUuid).uploadFiles([ + FileMetadata( + fileName: 'file.txt', + contentType: 'text/plain', + content: fileBytes, + ) + ], IFileUploadRequest()); + + // Get details of a specific file + var fileUuid = 'eaff2672-3012-46fb-9278-5efacc6cb616'; + var fileDetails = await storage.bucket(bucketUuid).file(fileUuid).get(); + print('File Details: ${fileDetails.name}, Size: ${fileDetails.size}'); +} +
IPNS methods
The Storage module additionally contains methods for manipulating IPNS records for a specific storage bucket.
import 'package:apillon_flutter/apillon_flutter.dart'; + +void main() async { + var storage = Storage(ApillonConfig( + key: 'yourApiKey', + secret: 'yourApiSecret', + )); + var bucketUuid = 'eaff2672-3012-46fb-9278-5efacc6cb616'; + + // List all existing IPNS records in a bucket + var ipnsRecords = await storage.bucket(bucketUuid).listIpnsNames(IPNSListRequest()); + print('IPNS Records:'); + for (var record in ipnsRecords) { + print('${record.name} - ${record.uuid}'); + } + + // Create a new IPNS record + const name = 'Test IPNS'; + const description = 'This is a test description'; + const cid = 'QmUxtfFfWFguxSWUUy2FiBsGuH6Px4KYFxJqNYJRiDpemj'; + var newIpnsRecord = await storage.bucket(bucketUuid).createIpns(ICreateIpns( + name: name, + description: description, + cid: cid, + )); + print('New IPNS Record: ${newIpnsRecord.uuid}'); + + // Publish an IPNS record to point to a new CID + const newCid = 'Qmakf2aN7wzt5u9H3RadGjfotu62JsDfBq8hHzGsV2LZFx'; + await storage.bucket(bucketUuid).ipns(newIpnsRecord.uuid).publish(newCid); + print('IPNS record published to new CID: $newCid'); + + // Delete an IPNS record + await storage.bucket(bucketUuid).ipns(newIpnsRecord.uuid).delete(); + print('IPNS record deleted: ${newIpnsRecord.uuid}'); +} +
NFTs
The NFT module encapsulates functionalities for the NFT service.
Usage example
import 'package:apillon_flutter/apillon_flutter.dart'; + +void main() async { + var nft = Nft(ApillonConfig( + key: 'yourApiKey', + secret: 'yourApiSecret', + )); + + // Create a new NFT collection + var collection = await nft.create(ICreateCollection( + chain1: EvmChain.moonbase, + collectionType1: CollectionType.generic, + name: 'Space Explorers', + description: 'A collection of space explorers', + symbol: 'SE', + royaltiesFees: 3, + royaltiesAddress: '0x95B8c6b9225456107649776EF8aAF20C42d58814', + baseUri: 'https://test.com/metadata/', + baseExtension: '.json', + maxSupply: 50, + isRevokable: false, + isSoulbound: false, + drop: false, + )); + print('Collection created: ${collection.uuid}'); + // or create a substrate collection + var substrateCollection = await nft.createSubstrate({ + chain1: SubstrateChain.astar, + collectionType1: CollectionType.generic, + name: 'SpaceExplorers', + symbol: 'SE', + ... + }); + + // Mint a new NFT in the collection + var mintResult = await nft.collection(collection.uuid).mint(IMintNftData( + receivingAddress: '0x5BA8B0c24bA5307b67E619ad500a635204F73bF1', + quantity: 1, + )); + print('Mint transaction hash: ${mintResult.transactionHash}'); + + // List NFT collections + var collections = await nft.listCollections(ICollectionFilters()); + + // Transfer NFT ownership to another address + await collection.transferOwnership('0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD'); +} +
Identity
The Identity module provides functionalities for validating wallet signatures and fetching identity data.
Usage example
import 'package:apillon_flutter/apillon_flutter.dart'; + +void main() async { + var identity = Identity(ApillonConfig( + key: 'yourApiKey', + secret: 'yourApiSecret', + )); + + // Generate a signing message for EVM wallet signature validation + const customMessage = 'Identity EVM SDK test'; + var signingMessage = identity.generateSigningMessage(customMessage)["message"]; + print('Signing message: $signingMessage'); + + var walletAddress = '0xa79bg13g2...'; + var signature = '0xYourSignature'; // signature obtained from the user's wallet by the client app + + // Validate EVM wallet signature + var validationResult = await identity.validateEvmWalletSignature(IValidateEvmWalletSignature( + walletAddress: walletAddress, + message: signingMessage, + signature: signature, + )); + print('Is valid: ${validationResult.isValid}'); + print('Address: ${validationResult.address}'); + + // Get wallet identity profile for a Polkadot address + var polkadotAddress = '5HqHQDGcHqS...', + var identityProfile = await identity.getWalletIdentity(polkadotAddress); + print('Identity Profile: ${identityProfile.subsocial['content']['name']}'); +} +
Storage API | Apillon Wiki + + + + ++ + + diff --git a/build/3-hosting-api.html b/build/3-hosting-api.html new file mode 100644 index 00000000..0711cba5 --- /dev/null +++ b/build/3-hosting-api.html @@ -0,0 +1,283 @@ + + + + + + + + +Apillon Wiki Storage API
In all cURL examples, parameters with a colon as a prefix should be replaced with real values.
File upload process through Apillon Web3 Storage API
- Request signed URL(s) for upload.
- File is uploaded to Apillon central server.
- File is transferred to IPFS and available through the Apillon gateway.
- File is replicated to different IPFS nodes globally via Crust Network.
List buckets
API to list all buckets in project. Items are paginated and can be filtered and ordered through query parameters. This is a listing request.
GET /storage/bucketsQuery parameters
All query parameters from listing request plus:
Name Description Required bucketType Type of bucket: 1
(storage bucket),2
(website bucket) and3
(nft bucket).false Response fields (bucket)
Each item is an instance of the bucket model with the below properties:
Field Type Description createTime DateTime
Item create time updateTime DateTime
Item last update time bucketUuid string
Bucket unique identifier bucketType integer
Item type with possible values 1
(storage bucket),2
(website bucket) and3
(nft bucket)name string
Bucket name description string
Bucket description size integer
Size of bucket in bytes curl --location --request GET "https://api.apillon.io/storage/buckets" \ +--header "Authorization: Basic :credentials" +
curl --location --request GET "https://api.apillon.io/storage/buckets?search=My bucket" \ +--header "Authorization: Basic :credentials" +
{ + "id": "75095bf9-e976-45c8-8a9d-e013ca3b203a", + "status": 200, + "data": { + "items": [ + ... + { + "createTime": "2022-12-06T12:03:32.000Z", + "updateTime": "2023-10-09T11:42:23.000Z", + "bucketUuid": "cee9f151-a371-495b-acd2-4362fbb87780", + "bucketType": 1, + "name": "Storage bucket", + "description": "", + "size": 3215839730 + } + ... + ], + "total": 10 + } +} +
Create new bucket
API for creating a new storage bucket. NFT and website buckets are automatically generated when a new website or NFT collection is initialized.
POST /storage/bucketsBody fields
Name Type Description Required name string
Bucket name. true description string
Bucket description. false Possible errors
Code Description 42200003 Request body is missing a name
field.Response
A response is an instance of bucket, described above.
curl --location --request POST "https://api.apillon.io/storage/buckets" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +--data-raw "{ \"name\": \"My bucket\" }" +
{ + "id": "e50c8276-fd8d-47c4-b42a-bf19645a204b", + "status": 201, + "data": { + "createTime": "2023-10-12T11:49:49.551Z", + "updateTime": "2023-10-12T11:49:49.551Z", + "bucketUuid": "8218080f-1321-4687-9a89-200b06afb930", + "bucketType": 1, + "name": "My bucket", + "description": null, + "size": 0 + } +} +
Upload to bucket
API that creates file upload requests and returns URLs for file upload along with
sessionUuid
.POST /storage/buckets/:bucketUuid/uploadURL parameters
Name Description Required bucketUuid Unique key of storage bucket. Key is displayed on developer dashboard. true Body fields
Name Type Description Required files array
Array of files metadata. Maximum 200 items. true sessionUuid string
Session unique key, which must be specified to add more uploads to existing session. false Each metadata object in the
files
array contains the properties below.
Name Type Description Required fileName string
Full name (name and extension) of file to be uploaded true contentType string
File MIME type false path string
Virtual file path. Empty for root. It must not contain fileName
.
Thepath
field can be used to place file in virtual directories inside a bucket. If directories do not yet exist, they will be automatically generated.
For example, animages/icons
path creates animages
directory in a bucket and anicons
directory inside it. A file will then be created in theicons
directory.false Possible errors
Code Description 40406002 Bucket does not exist. 40406009 Bucket is marked for deletion. It is no longer possible to upload files to it. 40006020 HTML files cannot be uploaded to storage bucket in freemium subscription plan. 42200040 Request body is missing a files
field.42200150 files
has invalid length. It should be between 1 and 20042200008 Request body file object is missing a fileName
field.50006003 Internal error - Apillon was unable to generate upload URL. Response
Name Type Description sessionUuid string
Session unique key, which is later used to end upload and transfer files to bucket files array
Array of files metadata. Files in the request body are returned in response
data.files
property. Each file is equipped withurl
andfileUuid
. All properties are displayed below.
Field Type Description url string
URL for file upload. Signed URL is unique for each file and is valid only for a limited time (1 min), so you should start with file upload as soon as possible.
Request should usePUT
method andbinary
body.
Binary data should be sent in body as-is, but with the appropriate Content-Type header (e.g., text/plain).fileUuid string
File unique identifier used to query file status, etc. fileName string
Full name (name and extension) of file to be uploaded contentType string
File MIME type path string
File path on the storage bucket. curl --location --request POST "https://api.apillon.io/storage/buckets/:bucketUuid/upload" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +--data-raw "{ + \"files\": [ + { + \"fileName\": \"My test file\", + \"contentType\": \"text/html\" + } + ] + +}" +
{ + "id": "cbdc4930-2bbd-4b20-84fa-15daa4429952", + "status": 201, + "data": { + "sessionUuid": "3b6113bc-f265-4662-8cc5-ea86f06cc74b", + "files": [ + { + "path": null, + "fileName": "My test file", + "contentType": "text/html", + "url": "https://sync-to-ipfs-queue.s3.eu-west-1.amazonaws.com/STORAGE_sessions/73/3b6113bc-f265-4662-8cc5-ea86f06cc74b/My%20test%20file?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAQIMRRA6GJRL57L7G%2F20230215%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20230215T114524Z&X-Amz-Expires=900&X-Amz-Signature=499367f6c6bff5be50686724475ac2fa6307b77b94fd1a25584c092fe74b0a58&X-Amz-SignedHeaders=host&x-id=PutObject", + "fileUuid": "4ef1177b-f7c9-4434-be56-a559cec0cc18" + } + ] + } +} +
Example for uploading to signed URL:
curl --location --request PUT "https://sync-to-ipfs-queue.s3.eu-west-1.amazonaws.com/STORAGE_sessions/73/3b6113bc-f265-4662-8cc5-ea86f06cc74b/My%20test%20file?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAQIMRRA6GJRL57L7G%2F20230215%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20230215T114524Z&X-Amz-Expires=900&X-Amz-Signature=499367f6c6bff5be50686724475ac2fa6307b77b94fd1a25584c092fe74b0a58&X-Amz-SignedHeaders=host&x-id=PutObject" \ +--data-binary "My test content" +
curl --location --request PUT "https://sync-to-ipfs-queue.s3.eu-west-1.amazonaws.com/STORAGE_sessions/73/3b6113bc-f265-4662-8cc5-ea86f06cc74b/My%20test%20file?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAQIMRRA6GJRL57L7G%2F20230215%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20230215T114524Z&X-Amz-Expires=900&X-Amz-Signature=499367f6c6bff5be50686724475ac2fa6307b77b94fd1a25584c092fe74b0a58&X-Amz-SignedHeaders=host&x-id=PutObject" \ +--header "Content-Type: text/plain" \ +--data-binary ":full path to file" +
End upload session
Once files are uploaded to the cloud server via the received URL, trigger file sync to IPFS and CRUST.
Note: Files in session can be wrapped to CID on IPFS via the
wrapWithDirectory
body field. This means that the directory gets its own CID and its content cannot be modified afterwards. The directory path is mandatory when thewrapWithDirectory
option is set totrue
. Read more about this option in the IPFS docsPOST /storage/buckets/:bucketUuid/upload/:sessionUuid/endURL parameters
Name Description Required bucketUuid Unique key of bucket. Key is displayed in developer dashboard. true sessionUuid Session uuid, recieved in upload to bucket true Body fields
Name Type Description Required wrapWithDirectory boolean
Wrap uploaded files to IPFS directory false directoryPath string
Path to wrapped directory inside bucket false Possible errors
Code Description 40406004 Session does not exist 40006001 Files in this session were already transferred Response
API responds with the status
200 OK
if operation is successfully executed.curl --location --request POST "https://api.apillon.io/storage/buckets/:bucketUuid/upload/:sessionUuid/end" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +
{ + "id": "b64b1c07-1a8a-4b05-9e3b-3c6a519d6ff7", + "status": 200, + "data": true +} +
List bucket content
Lists bucket directories and files in a folder structure. Endpoint lists files and directories in a single directory; if
directoryUuid
is not present, endpoint lists items in the bucket root directory. More about listing requests can be found hereNote: This endpoint returns files from ended sessions. I.e. files with fileStatus 2, 3 or 4.
GET /storage/buckets/:bucketUuid/contentURL parameters
Name Description Required bucketUuid Unique key of bucket. Key is displayed on developer dashboard. true Query parameters
All query parameters from listing request plus:
Name Description Required directoryUuid Gets items inside a specific directory. false markedForDeletion Include deleted buckets false Possible errors
Code Description 40406002 Bucket does not exist. Response fields
Properties of each item:
Field Type Description uuid string
Item UUID property type integer
Item type with possible values 1
(directory) and2
(file)name string
Item (directory or file) name CID string
File content identifier - label used to point to content in IPFS. createTime DateTime
Item create time updateTime DateTime
Item last update time contentType string
Item content type (MIME type). size integer
Item size in bytes directoryUuid string
Uuid of directory where the file directory is located link string
Link on IPFS gateway. fileStatus number
Current status of file curl --location --request GET "https://api.apillon.io/storage/buckets/:bucketUuid/content" \ +--header "Authorization: Basic :credentials" +
curl --location --request GET "https://api.apillon.io/storage/buckets/:bucketUuid/content?orderBy=name&desc=false&limit=5&page=1" \ +--header "Authorization: Basic :credentials" +
{ + "id": "c8c50b3b-91ff-42c7-b0af-f866ce23f18a", + "status": 200, + "data": { + "items": [ + ... + { + "uuid": "d61753fd-26ba-45cb-9277-89e96d6cfd11", + "type": 1, + "name": "Folder 1", + "CID": null, + "createTime": "2023-10-12T12:20:54.000Z", + "updateTime": "2023-10-12T12:20:54.000Z", + "contentType": null, + "size": null, + "directoryUuid": null, + "link": null, + "fileStatus": null + }, + { + "uuid": "63ace39b-ec7c-4889-8d94-83a2ad7fb154", + "type": 2, + "name": "My file.txt", + "CID": "QmaufbAR2dX62TSiYYJUS5sV9KNFZLnxgP4ZMkKFoJhSAM", + "createTime": "2023-10-12T12:17:19.000Z", + "updateTime": "2023-10-12T12:17:42.000Z", + "contentType": "", + "size": 6, + "directoryUuid": null, + "link": "https://ipfs-dev.apillon.io/ipfs/QmaufbAR2dX62TSiYYJUS5sV9KNFZLnxgP4ZMkKFoJhSAM", + "fileStatus": 3 + } + ... + ], + "total": 10 + } +} +
List files
Lists files inside a bucket. This endpoint returns all files in a flat structure and each file has a
path
property. More about listing requests can be found hereNote: This endpoint returns files from ended sessions. I.e. files with fileStatus 2, 3 or 4.
GET /storage/buckets/:bucketUuid/filesURL parameters
Name Description Required bucketUuid Unique key of bucket. Key is displayed on developer dashboard. true Possible errors
Code Description 40406002 Bucket does not exist. Response fields
Properties of each item:
Field Type Description createTime DateTime
File create time updateTime DateTime
File last update time fileUuid string
File UUID property CID string
File content identifier - label used to point to content in IPFS. name string
File name contentType string
File content type. Value is taken from file upload request path integer
Full path to file size integer
File size in bytes fileStatus number
File statuses are described in below table link string
Link on IPFS gateway. curl --location --request GET "https://api.apillon.io/storage/buckets/:bucketUuid/files" \ +--header "Authorization: Basic :credentials" +
curl --location --request GET "https://api.apillon.io/storage/buckets/:bucketUuid/files?search=Hello.txt" \ +--header "Authorization: Basic :credentials" +
{ + "id": "c8c50b3b-91ff-42c7-b0af-f866ce23f18a", + "status": 200, + "data": { + "items": [ + ... + { + "createTime": "2023-10-12T12:20:54.000Z", + "updateTime": "2023-10-13T06:08:00.000Z", + "fileUuid": "120afe0e-b146-45a5-82e0-52d2125df294", + "CID": "QmXKvPVY6jJ7e4oL3QcYjKFw6Bg7EKzzJAXCgXYjuCSyq5", + "name": "Hello.txt", + "contentType": "", + "path": "Folder 1/", + "size": 11, + "fileStatus": 3, + "link": "https://ipfs-dev.apillon.io/ipfs/QmXKvPVY6jJ7e4oL3QcYjKFw6Bg7EKzzJAXCgXYjuCSyq5" + }, + { + "createTime": "2023-10-12T12:17:19.000Z", + "updateTime": "2023-10-12T12:17:42.000Z", + "fileUuid": "63ace39b-ec7c-4889-8d94-83a2ad7fb154", + "CID": "QmaufbAR2dX62TSiYYJUS5sV9KNFZLnxgP4ZMkKFoJhSAM", + "name": "My file.txt", + "contentType": "", + "path": null, + "size": 6, + "fileStatus": 3, + "link": "https://ipfs-dev.apillon.io/ipfs/QmaufbAR2dX62TSiYYJUS5sV9KNFZLnxgP4ZMkKFoJhSAM" + } + ... + ], + "total": 10 + } +} +
Get file details
Gets details of a specific file inside a bucket.
GET /storage/buckets/:bucketUuid/files/:fileUuidURL parameters
Name Description required bucketUuid Unique key of a bucket. Key is displayed on developer dashboard. true fileUuid File UUID or CID. true Possible errors
Code Description 40406005 File does not exist. Response fields
Field Type Description createTime DateTime
File create time updateTime DateTime
File last update time fileUuid string
File UUID property CID string
File content identifier - label used to point to content in IPFS. name string
File name contentType string
File content type. Value is taken from file upload request path integer
Full path to file size integer
File size in bytes fileStatus number
File statuses are described in below table directoryUuid string
Uuid of directory where the file is located link string
Link on IPFS gateway. File statuses
Number Description 1 Request for upload to Apillon storage was generated. 2 File is uploaded to Apillon central server. 3 File is transferred to the IPFS node. 4 File is replicated to different IPFS nodes through Crust Network. curl --location --request GET "https://api.apillon.io/storage/buckets/:bucketUuid/files/:fileUuid" \ +--header "Authorization: Basic :credentials" +
{ + "id": "1beec975-9836-48b2-a284-591ae01f7a58", + "status": 200, + "data": { + "createTime": "2023-10-12T12:20:54.000Z", + "updateTime": "2023-10-12T12:21:17.000Z", + "fileUuid": "120afe0e-b146-45a5-82e0-52d2125df294", + "CID": "bafybeiefrfkhkevhdvacfjds7gw7mh2wlnuo66aeyffrik7wao5tlvfy3q", + "name": "Hello.txt", + "contentType": "", + "path": "Folder 1/", + "size": 11, + "fileStatus": 3, + "link": "https://ipfs-dev.apillon.io/ipfs/QmXKvPVY6jJ7e4oL3QcYjKFw6Bg7EKzzJAXCgXYjuCSyq5", + "directoryUuid": "d61753fd-26ba-45cb-9277-89e96d6cfd11" + } +} +
Delete file
Marks a file inside a bucket for deletion. The file will be completely deleted from the Apillon system and Apillon IPFS node after three (3) months. If a file is marked for deletion, it will not be renewed on Crust Network.
DELETE /storage/buckets/:bucketUuid/files/:fileUuidURL parameters
Name Description required bucketUuid Unique key of bucket. Key is displayed on developer dashboard. true fileUuid File unique identifier. true Possible errors
Code Description 40406005 File does not exist. 40006009 File is already marked for deletion. Response fields
The response of the delete function is a boolean value, depends on whether the deletion was successful.
curl --location --request DELETE "https://api.apillon.io/storage/buckets/:bucketUuid/files/:fileUuid" \ +--header "Authorization: Basic :credentials" \ +--data-raw "" +
{ + "id": "bc92ff8d-05f2-4380-bb13-75a1b6b7f388", + "status": 200, + "data": true +} +
Delete directory
Deletes a directory from the storage bucket.
DELETE storage/buckets/:bucketUuid/directories/:directoryUuidURL parameters
Name Description required bucketUuid Unique key of bucket. Key is displayed on developer dashboard. true directoryUuid Directory unique identifier. true Possible errors
Code Description 40406003 Directory does not exist. 40006007 Directory is already marked for deletion. Response fields
The response of the delete function is a boolean value, depends on whether the deletion was successful.
curl --location --request DELETE "https://api.apillon.io/storage/buckets/:bucketUuid/directories/:directoryUuid" \ +--header "Authorization: Basic :credentials" \ +--data-raw "" +
{ + "id": "bc92ff8d-05f2-4380-bb13-75a1b6b7f388", + "status": 200, + "data": true +} +
Get storage info
Gets overall storage info for a project.
Note: Available resources can be increased with a subscription to paid plans.
GET /storage/infoResponse fields
Field Type Description availableStorage integer
Available storage space in bytes usedStorage integer
Used storage in bytes. When usedStorage
reaches available storage, upload to buckets will be blocked (error 40006003)availableBandwidth integer
Monthly available bandwidth (upload and download) usedBandwidth integer
Bandwidth used in current month. If usedBandwidth
reaches available, requests to the IPFS gateway will be blockedcurl --location --request GET "https://api.apillon.io/storage/info" \ +--header "Authorization: Basic :credentials" +
{ + "id": "48e58f84-403a-43e4-bd81-fedeca195610", + "status": 200, + "data": { + "availableStorage": 3221225472, + "usedStorage": 1221225466, + "availableBandwidth": 3221225472, + "usedBandwidth": 56500 + } +} +
Get IPFS cluster info
Gets basic data of an IPFS cluster used by the project. IPFS clusters contain multiple IPFS nodes but expose a single gateway for accessing content via CID or IPNS. Apillon clusters (gateways) are not publicly accessible
Note: Each project has its own secret for the generation of the tokens to access content on the IPFS gateway.
GET /storage/ipfs-cluster-infoResponse fields
Field Type Description secret string
Secret for this project, which can be used to generate tokens to access content of IPFS gateway projectUuid string
Project unique identifier ipfsGateway string
Gateway that can used to access content via CIDs. ipnsGateway string
Gateway that can be used to access content via IPNS name. curl --location --request GET "https://api.apillon.io/storage/ipfs-cluster-info" \ +--header "Authorization: Basic :credentials" +
{ + "id": "0d7979c0-a00b-4502-9cb9-22228b24f71d", + "status": 200, + "data": { + "secret": "*********", + "project_uuid": "73f46f28-0d7c-43c4-9420-d4225b942ed1", + "ipfsGateway": "https://<CIDv1>.staging.nectarnode.io", + "ipnsGateway": "https://<IPNS>.staging.nectarnode.io" + } +} +
Get or generate link for IPFS
Apillon IPFS gateways are private and can only be accessible with a token. A token for specific address (CID) can be acquired via Apillon API request or can be generated with by using the
secret
andproject_uuid
properties from above requestGET /storage/link-on-ipfs/:cidURL parameters
Name Description required cid Ipfs content identifier. API will automatically detect the type (CIDv0, CIDv1 or IPNS) true Response fields
Field Type Description link string
Link where requested content can be accessed. How to generate token programmatically
Apillon IPFS gateways accept JWT token, which can be creates using jsonwebtoken package.
The JWT sign method expects three (3) parameters:
- JWT payload:
{ + "cid": "CID or IPNS address", + "project_uuid": "Change with projectUuid value" +} +
- Secret: Use
secret
property from IPFS cluster info- Subject:
IPFS-token
For each CID, a new token should be generated. Append the generated JWT to URL request as
token
query parameter.curl --location --request GET "https://api.apillon.io/storage/link-on-ipfs/:cid" \ +--header "Authorization: Basic :credentials" +
{ + "id": "3a3ea750-3f3a-41e3-b5cb-a5543c2b2283", + "status": 200, + "data": { + "link": "https://bafybeigjhyc2tpvqfqsuvf3byo4e4a4v6spi6jk4qqvvtlpca6rsaf2cqi.ipfs.nectarnode.io/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaWQiOiJiYWZ5YmVpZ2poeWMydHB2cWZxc3V2ZjNieW80ZTRhNHY2c3BpNmprNHFxdnZ0bHBjYTZyc2FmMmNxaSIsInByb2plY3RfdXVpZCI6IjE0NmM5ZWU5LTEwMDgtNDdiNS05ZTJjLTQxZmIyN2ExZjY1NSIsImlhdCI6MTcwMjU1NTA2Mywic3ViIjoiSVBGUy10b2tlbiJ9.07tHk5jAuAbcRaDxiiA9zHNWD71pxAcQX9v7LbhZ0-E" + } +} +
IPNS
List IPNS names
API to list all IPNS names in a bucket. Items are paginated and can be filtered and ordered through query parameters. This is a listing request.
GET /storage/buckets/:bucketUuid/ipnsURL parameters
Name Description Required bucketUuid Unique key of storage bucket, from which IPNS will be listed true Query parameters
All query parameters from listing request plus:
Name Description Required ipnsName List IPNS names with specific name false ipnsValue List IPNS names that point to this value (CID) false Response fields (ipns)
Each item is an instance of the IPNF model, with the following properties:
Field Type Description createTime DateTime
Item create time updateTime DateTime
Item last update time ipnsUuid string
IPNS unique identifier name string
Informational IPNS name, which is set by a user to easily organize the IPNS records description string
IPNS description ipnsName string
IPNS name used to access IPNS content on IPFs gateway ipnsValue string
IPFS value (CID), to which this ipns points link string
IPNS link to Apillon IPFS gateway, allowing to access content to which this IPNS points curl --location --request GET "https://api.apillon.io/storage/buckets/:bucketUuid/ipns" \ +--header "Authorization: Basic :credentials" +
curl --location --request GET "https://api.apillon.io/storage/buckets/:bucketUuid/ipns?ipnsName=k2k4r8lqt07ls9uyz141ofqcl99k4b8e63ns1fh52ib1bwh09z0k6vjk" \ +--header "Authorization: Basic :credentials" +
{ + "id": "f0764846-41f4-4352-87b4-85f9c94a8af4", + "status": 200, + "data": { + "items": [ + { + "createTime": "2023-11-24T06:22:16.000Z", + "updateTime": "2023-11-24T06:22:16.000Z", + "ipnsUuid": "9c0a0020-5d87-4112-a0ce-4033c037e31a", + "name": "IPNS from Apillon API", + "description": null, + "ipnsName": null, + "ipnsValue": null, + "link": null + }, + { + "createTime": "2023-11-24T13:43:43.000Z", + "updateTime": "2023-11-24T13:43:45.000Z", + "ipnsUuid": "0b3c4ca8-3054-42a2-b5d4-1665646bbaa0", + "name": "Example ipns", + "description": null, + "ipnsName": "k2k4r8lqt07ls9uyz141ofqcl99k4b8e63ns1fh52ib1bwh09z0k6vjk", + "ipnsValue": "/ipfs/Qma6zTc8ctd65U2SARH7Qkssm6KrwsqJnX1PtrSqhXcM9L", + "link": "https://ipfs-eu1.apillon.io/ipns/k2k4r8lqt07ls9uyz141ofqcl99k4b8e63ns1fh52ib1bwh09z0k6vjk/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaWQiOiJrMms0cjhscXQwN2xzOXV5ejE0MW9mcWNsOTlrNGI4ZTYzbnMxZmg1MmliMWJ3aDA5ejBrNnZqayIsInByb2plY3RfdXVpZCI6ImQ3ZTlkZjQwLTcxNDgtNGYwZC1hMTEyLTM5YmYzMjY5NWFlNCIsImlhdCI6MTcwMDk4MTg3Niwic3ViIjoiSVBGUy10b2tlbiJ9.LMRhNNtsYF-0NlIcXFL1O85I58bsC_zHlbAepPz0hVM" + } + ], + "total": 2 + } +} +
Create new IPNS
API for creating a new IPNS record.
Note: IPNS becomes accesible on the IPFS gateway when content with CID is published to it. To access IPNS content on the IPFS gateway, use
ipnsName
.POST /storage/buckets/:bucketUuid/ipnsURL parameters
Name Description Required bucketUuid Unique key of storage bucket where IPNS will be created. Key is displayed on developer dashboard. true Body fields
Name Type Description Required name string
IPNS name. true description string
IPNS description. false cid string
CID to which the IPNS name will point. If this property is specified, API executes IPNS publish, which sets the ipnsName
andipnsValue
properties.false Possible errors
Code Description 42200026 Request body is missing a name
field.40406002 Bucket not found Response
Response is an instance of IPNS described above.
curl --location --request POST "https://api.apillon.io/storage/buckets/:bucketUuid/ipns" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +--data-raw "{ + \"name\": \"Example IPNS\", + \"cid\": \"Qma6zTc8ctd65U2SARH7Qkssm6KrwsqJnX1PtrSqhXcM9L\" +}" +
{ + "id": "0f436448-7c05-4f29-ae49-c57f55e36705", + "status": 201, + "data": { + "createTime": "2023-11-24T12:14:31.127Z", + "updateTime": null, + "ipnsUuid": "0b3c4ca8-3054-42a2-b5d4-1665646bbaa0", + "projectUuid": "d7e9df40-7148-4f0d-a112-39bf32695ae4", + "bucketId": 11, + "name": "Example ipns", + "description": null, + "ipnsName": "k2k4r8lqt07ls9uyz141ofqcl99k4b8e63ns1fh52ib1bwh09z0k6vjk", + "ipnsValue": "/ipfs/Qma6zTc8ctd65U2SARH7Qkssm6KrwsqJnX1PtrSqhXcM9L", + "link": "https://ipfs-eu1.apillon.io/ipns/k2k4r8lqt07ls9uyz141ofqcl99k4b8e63ns1fh52ib1bwh09z0k6vjk/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaWQiOiJrMms0cjhscXQwN2xzOXV5ejE0MW9mcWNsOTlrNGI4ZTYzbnMxZmg1MmliMWJ3aDA5ejBrNnZqayIsInByb2plY3RfdXVpZCI6ImQ3ZTlkZjQwLTcxNDgtNGYwZC1hMTEyLTM5YmYzMjY5NWFlNCIsImlhdCI6MTcwMDk4MTg3Niwic3ViIjoiSVBGUy10b2tlbiJ9.LMRhNNtsYF-0NlIcXFL1O85I58bsC_zHlbAepPz0hVM" + } +} +
Get IPNS
API to get specific IPNS name by its UUID.
GET /storage/buckets/:bucketUuid/ipns/:ipnsUuidURL parameters
Name Description Required bucketUuid Unique key of storage bucket true ipnsUuid Unique key of IPNS name true Possible errors
Code Description 40406012 IPNS not found Response fields (IPNS)
Response is an instance of IPNS described above.
curl --location --request GET "https://api.apillon.io/storage/buckets/:bucketUuid/ipns/:ipnsUuid" \ +--header "Authorization: Basic :credentials" +
{ + "id": "0f436448-7c05-4f29-ae49-c57f55e36705", + "status": 201, + "data": { + "createTime": "2023-11-24T12:14:31.127Z", + "updateTime": null, + "ipnsUuid": "0b3c4ca8-3054-42a2-b5d4-1665646bbaa0", + "projectUuid": "d7e9df40-7148-4f0d-a112-39bf32695ae4", + "bucketId": 11, + "name": "Example IPNS", + "description": null, + "ipnsName": "k2k4r8lqt07ls9uyz141ofqcl99k4b8e63ns1fh52ib1bwh09z0k6vjk", + "ipnsValue": "/ipfs/Qma6zTc8ctd65U2SARH7Qkssm6KrwsqJnX1PtrSqhXcM9L", + "link": "https://ipfs-eu1.apillon.io/ipns/k2k4r8lqt07ls9uyz141ofqcl99k4b8e63ns1fh52ib1bwh09z0k6vjk/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaWQiOiJrMms0cjhscXQwN2xzOXV5ejE0MW9mcWNsOTlrNGI4ZTYzbnMxZmg1MmliMWJ3aDA5ejBrNnZqayIsInByb2plY3RfdXVpZCI6ImQ3ZTlkZjQwLTcxNDgtNGYwZC1hMTEyLTM5YmYzMjY5NWFlNCIsImlhdCI6MTcwMDk4MTg3Niwic3ViIjoiSVBGUy10b2tlbiJ9.LMRhNNtsYF-0NlIcXFL1O85I58bsC_zHlbAepPz0hVM" + } +} +
Publish IPNS
API for publishing IPNS on IPFS and linking it to CID.
Note: Multiple IPNS records can point to the same CID.
POST /storage/buckets/:bucketUuid/ipns/:ipnsUuid/publishURL parameters
Name Description Required bucketUuid Unique key of storage bucket. true ipnsUuid Unique key of IPNS record that will be published true Body fields
Name Type Description Required cid string
CID to which the IPNS name will point. true Possible errors
Code Description 42200030 Body is missing CID
property40406012 IPNS not found Response
The response is an instance of IPNS that was published. Properties are described above.
curl --location --request POST "https://api.apillon.io/storage/buckets/:bucketUuid/ipns/:ipnsUuid/publish" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +--data-raw "{ + \"cid\": \"Qma6zTc8ctd65U2SARH7Qkssm6KrwsqJnX1PtrSqhXcM9L\" +}" +
{ + "id": "14bfe999-ffc4-477b-bf10-4fe9ba9ab90a", + "status": 200, + "data": { + "createTime": "2023-11-26T07:13:32.000Z", + "updateTime": "2023-11-26T07:13:32.000Z", + "ipnsUuid": "df5c47b4-e00b-4163-877e-7c78042e7666", + "name": "My 3 IPNS", + "description": null, + "ipnsName": "k2k4r8jofss9us61kwlmq8flgdhj3a1tn5ikcc6m494kf2edifi2oh4z", + "ipnsValue": "/ipfs/Qma6zTc8ctd65U2SARH7Qkssm6KrwsqJnX1PtrSqhXcM9L", + "link": "https://ipfs-eu1.apillon.io/ipns/k2k4r8jofss9us61kwlmq8flgdhj3a1tn5ikcc6m494kf2edifi2oh4z/?token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaWQiOiJrMms0cjhqb2Zzczl1czYxa3dsbXE4ZmxnZGhqM2ExdG41aWtjYzZtNDk0a2YyZWRpZmkyb2g0eiIsInByb2plY3RfdXVpZCI6ImQ3ZTlkZjQwLTcxNDgtNGYwZC1hMTEyLTM5YmYzMjY5NWFlNCIsImlhdCI6MTcwMTA3NTk3OCwic3ViIjoiSVBGUy10b2tlbiJ9.xJ0ZdUb0XqH9oe7AvG0yUnCnydKNoGlNnNsIYZEwAc0" + } +} +
Delete IPNS
API to delete IPNS record.
DELETE /storage/buckets/:bucketUuid/ipns/:ipnsUuidURL parameters
Name Description Required bucketUuid Unique key of storage bucket true ipnsUuid Unique key of IPNS record true Possible errors
Code Description 40406012 IPNS not found Response fields (IPNS)
The response is deleted IPNS record, an instance of IPNS described above.
curl --location --request DELETE "https://api.apillon.io/storage/buckets/:bucketUuid/ipns/:ipnsUuid" \ +--header "Authorization: Basic :credentials" +
{ + "id": "302f036b-5ca5-488c-bbd5-a7cdd7674898", + "status": 200, + "data": { + "createTime": "2023-11-24T06:22:16.000Z", + "updateTime": "2023-11-24T06:22:16.000Z", + "ipnsUuid": "9c0a0020-5d87-4112-a0ce-4033c037e31a", + "name": "IPNS from Apillon API", + "description": null, + "ipnsName": null, + "ipnsValue": null + } +} +
Hosting API | Apillon Wiki + + + + ++ + + diff --git a/build/4-nfts-api.html b/build/4-nfts-api.html new file mode 100644 index 00000000..a7e6d7f1 --- /dev/null +++ b/build/4-nfts-api.html @@ -0,0 +1,290 @@ + + + + + + + + +Apillon Wiki Hosting API
Hosting API provides endpoints for listing and deployment of websites. API can be used to implement CI/CD. To deploy page through Apillon API, follow below steps:
- Upload website files to Apillon cloud server.
- Request URLs for files upload
- Upload files to cloud server
- Trigger transfer into website
- Execute deployment to staging or production environment.
Note: You should first create a website on the Apillon dashboard.
In all cURL examples, parameters with a colon as a prefix should be replaced with real values.
List websites
API to list all websites in project. Items are paginated and can be filtered and ordered through query parameters as described here .
GET /hosting/websitesResponse fields (website)
Each item is an instance of website class, with below properties:
Field Type Description websiteUuid string
Website unique identifier name string
Website name description string
Website description domain string
Website domain. This property needs to be specified, so that Apillon is able to create SSL Certificates for IPFS gateway bucketUuid string
Uuid of bucket for file upload ipnsStaging string
Staging IPNS. Set if deployment to staging environment exists ipnsProduction string
Production IPNS. createTime DateTime
Item create time updateTime DateTime
Item last update time curl --location --request GET "https://api.apillon.io/hosting/websites" \ +--header "Authorization: Basic :credentials" +
curl --location --request GET "https://api.apillon.io/storage/buckets?search=My website" \ +--header "Authorization: Basic :credentials" +
{ + "id": "75095bf9-e976-45c8-8a9d-e013ca3b203a", + "status": 200, + "data": { + "items": [ + ... + { + "createTime": "2023-10-11T10:51:13.000Z", + "updateTime": "2023-10-11T10:51:13.000Z", + "websiteUuid": "5fc7df41-f311-410d-8cb3-998198999a48", + "name": "My website", + "description": null, + "domain": null, + "bucketUuid": "1938a45c-3a54-43ee-af08-3abe90265f46", + "ipnsStaging": null, + "ipnsProduction": null + } + ... + ], + "total": 10 + } +} +
Get website
Endpoint to get website. Endpoint returns basic website data, along with IPNS links.
GET /hosting/websites/:websiteUuidURL parameters
Name Description Required websiteUuid Website UUID, visible in developer console website overview true Possible errors
Code Description 40406010 Website does not exists Response fields
Response is an instance of website class, described above and additional properties described below. Those properties will receive value after deploy.
Field Type Description w3StagingLink string
Link to staging version of the website w3ProductionLink string
Link to production version of the website lastDeploymentUuid string
Website last deployment (to any environment) unique identifier lastDeploymentStatus string
Status of last deployment curl --location --request GET "https://api.apillon.io/hosting/websites/:websiteUuid" \ +--header "Authorization: Basic :credentials" +
{ + "id": "a299ed8f-b682-4411-9fd9-eb3a3da31887", + "status": 200, + "data": { + "createTime": "2023-10-13T07:41:30.000Z", + "updateTime": "2023-10-13T07:41:30.000Z", + "websiteUuid": "1a15d258-bbc9-459f-b83f-97710da6b983", + "name": "My awesome website", + "description": "My unstoppable website", + "domain": "example-domain.io", + "bucketUuid": "cd299839-dae6-47d0-8fdc-40143163e156", + "ipnsStaging": null, + "ipnsProduction": null, + "w3StagingLink": null, + "w3ProductionLink": null, + "lastDeploymentUuid": null, + "lastDeploymentStatus": null + } +} +
Get URLs for files upload
API that creates file upload requests and returns URLs for files upload.
POST /hosting/websites/:websiteUuid/uploadURL parameters
Name Description Required websiteUuid Unique key of website bucket. Key is displayed on developer dashboard. true Body fields
Name Type Description Required files array
Array of files metadata. true sessionUuid string
Session unique key. If not specified, API generates new one. It is possible to use same sessionUuid in multiple requests. false Each file metadata object in
files
array, contain below properties.
Name Type Description Required fileName string
Full name (name and extension) of file to be uploaded true contentType string
File MIME type false path string
File path inside website. Empty for root. Must not contain fileName
.false Possible errors
Code Description 40406010 Website does not exists 42200040 Request body is missing a files
field.42200008 Request body file object is missing a fileName
field.50006003 Internal error - Apillon was unable to generate upload URL. Response
Name Type Description sessionUuid string
Session unique key, which is later used to end upload and transfer files to website. files array
Array of files metadata. Files in request body are returned in response
data.files
property. Each file is equipped withurl
andfileUuid
. All properties are displayed below.
Field Type Description url string
URL for file upload. Signed URL is unique for each file and is valid only for a limited time (1 min), so you should start with file upload as soon as possible.
Request should usePUT
method andbinary
body.
Binary data should be sent in body as-is, but with the appropriate Content-Type header (e.g., text/plain).fileUuid string
File unique identifier used to query file status, etc. fileName string
Full name (name and extension) of file to be uploaded contentType string
File MIME type path string
File path on the hosting bucket. curl --location --request POST "https://api.apillon.io/hosting/websites/:websiteUuid/upload" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +--data-raw "{ + \"files\": [ + { + \"fileName\": \"index.html\", + \"contentType\": \"text/html\" + }, + { + \"fileName\": \"styles.css\", + \"contentType\": \"text/css\", + \"path\": \"assets/\" + } + + ] + +}" +
{ + "id": "7dd011ec-20e2-4c28-b585-da6c6f7fce8d", + "status": 201, + "data": { + "sessionUuid": "29ef6ca2-b171-440c-b934-db8aa88c3424", + "files": [ + { + "path": null, + "fileName": "index.html", + "contentType": "text/html", + "url": "https://sync-to-ipfs-queue.s3.eu-west-1.amazonaws.com/HOSTING_sessions/70/29ef6ca2-b171-440c-b934-db8aa88c3424/index.html?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAQIMRRA6GJRL57L7G%2F20230215%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20230215T105030Z&X-Amz-Expires=900&X-Amz-Signature=187035d2307bc089101eff3abbdd7baa3e8691b4d5d3bafa5aebb87e589e8c0c&X-Amz-SignedHeaders=host&x-id=PutObject", + "fileUuid": "e17436a1-5292-4380-ad91-eaac02a862b1" + }, + { + "path": "assets/", + "fileName": "styles.css", + "contentType": "text/css", + "url": "https://sync-to-ipfs-queue.s3.eu-west-1.amazonaws.com/HOSTING_sessions/70/29ef6ca2-b171-440c-b934-db8aa88c3424/assets/styles.css?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAQIMRRA6GJRL57L7G%2F20230215%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20230215T105030Z&X-Amz-Expires=900&X-Amz-Signature=f91b03a951fe3f99291802306be3e79812ca64e39effbb7dea1c19bb7cd1e42b&X-Amz-SignedHeaders=host&x-id=PutObject", + "fileUuid": "358c2942-4ced-421e-9a6f-edbf94c55dff" + } + ] + } +} +
Example for uploading to signed URL:
curl --location --request PUT "https://sync-to-ipfs-queue.s3.eu-west-1.amazonaws.com/HOSTING_sessions/70/29ef6ca2-b171-440c-b934-db8aa88c3424/index.html?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAQIMRRA6GJRL57L7G%2F20230215%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20230215T105030Z&X-Amz-Expires=900&X-Amz-Signature=187035d2307bc089101eff3abbdd7baa3e8691b4d5d3bafa5aebb87e589e8c0c&X-Amz-SignedHeaders=host&x-id=PutObject" \ +--header "Content-Type: text/plain" \ +--data-raw "<h1> +Welcome to my awesome website +</h1>" +
curl --location --request PUT "https://sync-to-ipfs-queue.s3.eu-west-1.amazonaws.com/HOSTING_sessions/70/29ef6ca2-b171-440c-b934-db8aa88c3424/index.html?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAQIMRRA6GJRL57L7G%2F20230215%2Feu-west-1%2Fs3%2Faws4_request&X-Amz-Date=20230215T105030Z&X-Amz-Expires=900&X-Amz-Signature=187035d2307bc089101eff3abbdd7baa3e8691b4d5d3bafa5aebb87e589e8c0c&X-Amz-SignedHeaders=host&x-id=PutObject" \ +--header "Content-Type: text/plain" \ +--data-binary ":full path to file" +
End upload session
Transfer files to website bucket, which is used as source for deploy to staging(preview) environment.
POST /hosting/websites/:websiteUuid/upload/:sessionUuid/endURL parameters
Name Description Required websiteUuid Unique key of website. true sessionUuid Session uuid, passed or recieved in get URL for upload request true Possible errors
Code Description 40406004 Session does not exists 40006001 Files in this session were already transferred Response
Api respond with status
200 OK
, if operation is successfully executed.curl --location --request POST "https://api.apillon.io/hosting/websites/:websiteUuid/upload/:sessionUuid/end" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +
{ + "id": "b64b1c07-1a8a-4b05-9e3b-3c6a519d6ff7", + "status": 200, + "data": true +} +
Deploy website
Endpoint to trigger website deployment into specific environment.
POST /hosting/websites/:websiteUuid/deployURL parameters
Name Description Required websiteUuid Website UUID, visible in developer console website overview true Body fields
Name Type Description Required environment number
Possible environment
values are explained belowtrue Environments
Status Description 1 Uploaded files are deployed to staging(preview) environment. Website will be available through staging IPNS link 2 Files from current staging environment are deployed to production environment. Website is pinned to CRUST, replicated and available through production IPNS link 3 Same as 2
, only that the source are uploaded files, not files in staging environment.Possible errors
Code Description 42200039 Request body is missing an environment
field.40406010 Website does not exists 40006016 There are no files to deploy. 40006017 There are no changes to deploy. Response
Endpoint triggers deployment of website to specific environment. As result, deployment record with below field is returned. This deployment is now waiting to be processed.
Note: Deployment is processed in background, which may take several minutes. When deploying to
staging
environment, files are added to IPFS and wrapped to directory, which is then accessible in IPFS via IPNS or CID. Inproduction
, this CID is pinned to CRUST and replicated to other nodes.Deployment fields
Field Type Description deploymentUuid string
Deployment unique identifier environment number
Environment to where website will be deployed deploymentStatus number
Current status of deployment. Possible values are listed below. cid string
When deployment is successful, CID points to directory on IPFS, where this page is accessible cidv1 string
CID version 1 size number
Size of website number number
Deployment serial number - for this environment createTime DateTime
Deployment create time updateTime DateTime
Deployment last update time Website deployment statuses
Deployment goes through different stages and each stage updates
deploymentStatus
. Possible deployment statuses:
Status Description 0 Deployment initiated 1 In processing 2 In review 3 Website approved. Deployment will be executed 10 Deployment successful 100 Deployment failed 101 Deployment rejected Deployments (to environments 1 and 3) in projects without subscription go to review (
deploymentStatus 3
). Review can take some time (up to 1 day) and if the website passes it, then the deployment continues.Websites with illegal/phishing content goes to status
101
. The project owner is notified via mail and most likely banned from Apillon platform.To speed up deployment process, make sure that project has one of subscription packages.
curl --location --request POST "https://api.apillon.io/hosting/websites/:websiteUuid/deploy" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +--data-raw "{ + \"environment\": 1 +}" +
{ + "id": "dfe79ed9-e2fe-4195-b829-053dee1e6fd1", + "status": 200, + "data": { + "createTime": "2023-10-13T12:23:51.721Z", + "updateTime": "2023-10-13T12:23:51.721Z", + "deploymentUuid": "583790dc-7b56-4563-8a33-d88243eed11e", + "environment": 1, + "deploymentStatus": 0, + "cid": null, + "cidv1": null, + "size": null, + "number": 1 + } +} +
List website deployments
API to list website deployments. More about listing requests can be found here
GET /hosting/websites/:websiteUuid/deploymentsURL parameters
Name Description Required deploymentStatus Current deployment status false environment Deployment environment false Response fields
Each item in list is a deployment instance.
curl --location --request GET "https://api.apillon.io/hosting/websites/:websiteUuid/deployments" \ +--header "Authorization: Basic :credentials" +
curl --location --request GET "https://api.apillon.io/hosting/websites/:websiteUuid/deployments?orderBy=number" \ +--header "Authorization: Basic :credentials" +
{ + "id": "75095bf9-e976-45c8-8a9d-e013ca3b203a", + "status": 200, + "data": { + "items": [ + ... + { + "createTime": "2023-10-13T12:28:39.000Z", + "updateTime": "2023-10-13T12:29:15.000Z", + "deploymentUuid": "12b47aef-6a01-4799-a4da-79ea7595237d", + "environment": 2, + "deploymentStatus": 10, + "cid": "QmTF31ediusaBTNn2Z1Jtr5fF1iVp9oDA8EWz9my6aVx8V", + "cidv1": "bafybeici3ivncsgfszf7tbmttayyjk6hs25354zs5nczujv44rco4qiv7y", + "size": 41, + "number": 1 + }, + { + "createTime": "2023-10-13T12:23:51.000Z", + "updateTime": "2023-10-13T12:24:17.000Z", + "deploymentUuid": "583790dc-7b56-4563-8a33-d88243eed11e", + "environment": 1, + "deploymentStatus": 10, + "cid": "QmTF31ediusaBTNn2Z1Jtr5fF1iVp9oDA8EWz9my6aVx8V", + "cidv1": "bafybeici3ivncsgfszf7tbmttayyjk6hs25354zs5nczujv44rco4qiv7y", + "size": 41, + "number": 1 + } + ... + ], + "total": 10 + } +} +
Get deployment
Endpoint to get deployment.
GET /hosting/websites/:websiteUuid/deployments/:deployment_uuidURL parameters
Name Description Required websiteUuid Website UUID, visible in developer console website overview true deploymentUuid Deployment unique identifier, returned from deploy
website endpointtrue Possible errors
Code Description 40406011 Deployment does not exists Response fields
Data
property is a deployment instance.curl --location --request GET "https://api.apillon.io/hosting/websites/:websiteUuid/deployments/:deploymentUuid" \ +--header "Authorization: Basic :credentials" +
{ + "id": "2d7d1b0c-15b1-4816-9aec-857182c7b617", + "status": 200, + "data": { + "createTime": "2023-10-13T12:23:51.000Z", + "updateTime": "2023-10-13T12:24:17.000Z", + "deploymentUuid": "583790dc-7b56-4563-8a33-d88243eed11e", + "environment": 1, + "deploymentStatus": 10, + "cid": "QmTF31ediusaBTNn2Z1Jtr5fF1iVp9oDA8EWz9my6aVx8V", + "cidv1": "bafybeici3ivncsgfszf7tbmttayyjk6hs25354zs5nczujv44rco4qiv7y", + "size": 41, + "number": 1 + } +} +
NFTs API | Apillon Wiki + + + + ++ + + diff --git a/build/5-apillon-sdk.html b/build/5-apillon-sdk.html new file mode 100644 index 00000000..e4c0055b --- /dev/null +++ b/build/5-apillon-sdk.html @@ -0,0 +1,375 @@ + + + + + + + + +Apillon Wiki NFTs API
API is for creating and managing NFTs. To prepare images and metadata for your NFT collection you can use the Storage API. To learn more about metadata standards you can visit: https://docs.opensea.io/docs/metadata-standards
Get NFT Collection
Get NFT collection by UUID
GET /nfts/collections/:uuidURL parameters
Name Description Required uuid Unique key of collection. Key is displayed in developer dashboard. true Possible errors
Code Description 40300000 Not allowed to access collection. 50012009 Collection does not exist. Response Fields
Name Type Description createTime DateTime
Collection create time. updateTime DateTime
Collection last update time. collectionType number
Type of smart contract to use for collection. Available types are described here. collectionUuid string
Unique key of a collection. symbol string
NFT collection symbol (usually 3-4 characters long). name string
NFT collection name. description string
NFT collection description. maxSupply number
Maximal number of NFTs ever in existence (0 stands for unlimited). bucketUuid string
UUID of the bucket where metadata is stored. baseUri string
Base URI for collection metadata (token id and file extension is appended to it). baseExtension string
File extension that is auto appended after token id to form a full URL. isSoulbound boolean
Soul bound tokens are NFTs that are bounded to wallet and not transferable. isRevokable boolean
For revocable collection owner can destroy NFTs at any time. isAutoIncrement boolean
If set to false, enables minting NFTs with a custom token ID royaltiesFees number
Percentage (between 0 and 100) of each NFT sale sent to wallet specified under royalties address. royaltiesAddress string
Address where royalties are sent to. collectionStatus number
Apillon internal/database collection status. contractAddress string
Smart address of contract for deployed collection. transactionHash string
Deployment transaction hash/id. deployerAddress string
Wallet address of deployer. chain number
Blockchain id on which you want to release your collection. drop boolean
Determines if collection is mintable by public. dropStart number
UNIX timestamp which determines public mint opening date and time. dropPrice number
Price of NFT at mint stage in token that is used on chain
.dropReserve number
Amount of NFTs reserved by owner. curl --location 'https://api.apillon.io/nfts/collections/:uuid' \ +--header 'Authorization: Basic :credentials' +
{ + "id": "b5935c73-204d-4365-9f9a-6a1792adab5b", + "status": 200, + "data": { + "createTime": "2023-06-13T10:15:58.000Z", + "updateTime": "2023-06-13T10:15:58.000Z", + "collectionType": 1, + "collectionUuid": "d6355fd3-640d-4803-a4d9-79d875abcb5a", + "symbol": "NFT", + "name": "NFT Collection", + "description": "NFT Collection Description", + "maxSupply": 1000, + "bucketUuid": "a9425ff7-4802-4a38-b771-84a790112c30", + "baseUri": "https://ipfs.apillon.io/metadata/", + "baseExtension": ".json", + "isSoulbound": false, + "isRevokable": true, + "royaltiesFees": 0.1, + "royaltiesAddress": "0x4156edbafc5091507de2dd2a53ded551a346f83b", + "collectionStatus": 0, + "contractAddress": "0x452101C96A1Cf2cBDfa5BB5353e4a7F235241557", + "transactionHash": "0x6b97424de3367cd0335b08265787b83053b62bee2d1c8bec1f776936bea4fb26", + "deployerAddress": "0x4156edbafc5091507de2dd2a53ded551a346f83b", + "chain": 1287, + "drop": true, + "dropStart": 1687251003, + "dropPrice": 0.1, + "dropReserve": 5 + } +} +
Collection Type
Number Description 0 Generic NFT collection. 1 Nestable NFT collection. Collection Statuses
Number Description 0 Collection was created. 1 Deploying collection was initiated. 2 Collection is being deployed. 3 Collection was deployed successfully. 4 Collection was transferred successfully. 5 Failed deploying collection. List NFT Collections
List NFT collections. Items are paginated and can be filtered and ordered through query parameters.
GET /nfts/collectionsQuery parameters
All query parameters from listing request plus:
Name Description Required collectionStatus Collection status. Find available statuses here. false Response
Response is a list of items described under Response Fields above.
curl --location 'https://api.apillon.io/nfts/collections' \ +--header 'Authorization: Basic :credentials' +
{ + "id": "b5935c73-204d-4365-9f9a-6a1792adab5b", + "status": 200, + "data": { + "items": [ + { + "createTime": "2023-06-13T10:15:58.000Z", + "updateTime": "2023-06-13T10:15:58.000Z", + "collectionType": 1, + "collectionUuid": "d6355fd3-640d-4803-a4d9-79d875abcb5a", + "symbol": "NFT", + "name": "NFT Collection", + "description": "NFT Collection Description", + "maxSupply": 1000, + "bucketUuid": "a9425ff7-4802-4a38-b771-84a790112c30", + "baseUri": "https://ipfs.apillon.io/metadata/", + "baseExtension": ".json", + "isSoulbound": false, + "isRevokable": true, + "royaltiesFees": 0.1, + "royaltiesAddress": "0x4156edbafc5091507de2dd2a53ded551a346f83b", + "collectionStatus": 0, + "contractAddress": "0x452101C96A1Cf2cBDfa5BB5353e4a7F235241557", + "transactionHash": "0x6b97424de3367cd0335b08265787b83053b62bee2d1c8bec1f776936bea4fb26", + "deployerAddress": "0x4156edbafc5091507de2dd2a53ded551a346f83b", + "chain": 1287, + "drop": true, + "dropStart": 1687251003, + "dropPrice": 0.1, + "dropReserve": 5 + } + ], + "total": 1 + } +} +
List Collection Transactions
List NFT collections. Items are paginated and can be filtered and ordered through query parameters.
GET /nfts/collections/:uuid/transactionsURL parameters
Name Description Required uuid Unique key of collection. Key is displayed in developer dashboard. true Query parameters
All query parameters from listing request plus:
Name Description Required transactionStatus Transaction status. false transactionType Transaction type. false Response Fields
Name Type Description chainId number
Blockchain id on which you want to release your collection. transactionType number
Transaction type. transactionStatus number
Transaction status transactionHash number
Transaction hash/id. updateTime DateTime
Transaction last update time. createTime DateTime
Transaction create time. curl --location 'https://api.apillon.io/nfts/collections/:uuid/transactions' \ +--header 'Authorization: Basic :credentials' +
{ + "id": "b5935c73-204d-4365-9f9a-6a1792adab5b", + "status": 200, + "data": { + "items": [ + { + "chainId": 1287, + "transactionType": 1, + "transactionStatus": 1, + "transactionHash": "0xb988c8cda7ec8b441611b208360e0aace9c294e1ca5672375b21e815890a54d1", + "updateTime": "2023-06-13T10:15:58.000Z", + "createTime": "2023-06-13T10:15:58.000Z" + } + ], + "total": 1 + } +} +
Transaction Types
Number Description 1 Deploy Contract 2 Transfer Contract Ownership 3 Mint NFT 4 Set Collection Base URI 5 Burn NFT 6 Nest mint NFT Transaction Status
Number Description 1 Pending 2 Confirmed 3 Failed 4 Error Create NFT Collection
API endpoint that creates NFT collection and deploys it on selected network.
An NFT Collection can be created with a few features/functionalities:
- drop: collection can be minted/purchased by users
- revokable: NFTs can be revoked by collection owner who can burn them
- soulbound: NFTs are bound to wallet address and can't be transferred
- royalties: owner can enable royalties to earn specified percentage per each NFT trade
2 types of collections are supported:
- Generic collection, which represents an extension of the ERC-721 standard for EVM collections and the PSP-34 standard for substrate collections. You can read more about these standards here
- Nestable collection which allows nesting NFTs under each other (based on RMRKs ERC-7401 NFT standard - EVM only)
Additionally, 2 chain types/environments are supported: EVM and Substrate.
Create Substrate NFT Collection
POST /nfts/collections/substrateBody fields
Name Type Description Required collectionType number
Type of smart contract to use when deploying collection (1 for generic, 2 for nestable). true chain number
Blockchain ID on which you want to release your collection. Options: ( 8
- Astar)true symbol string
NFT collection symbol (usually 3-4 characters long). true name string
NFT collection name. true description string
NFT collection description. false maxSupply number
Maximal number of NFTs ever in existence (0 stands for unlimited). true baseUri string
Base URI for collection metadata (token id and file extension is appended to it). true baseExtension string
File extension that is auto appended after token id to form a full URL. true royaltiesAddress string
Address where royalties are sent to. true royaltiesFees number
Percentage of royalties earned per each NFT trade. true drop boolean
Determines if collection is mintable by public. true dropStart* number
UNIX timestamp (in seconds) which determines public mint opening date and time. true dropPrice* number
Price of NFT at mint stage. true dropReserve* number
Amount of NFTs reserved by owner. true Notes:
*
dropStart
,dropPrice
anddropReserve
are only used ifdrop
is set to booleantrue
.Possible errors
Beside validation errors (with 422 http status code) these are the error codes may be returned:
Code Description 40012002 Collection quota reached 50012003 Failed deploying NFT contract on chain. 50012010 Failed to create bucket for storing metadata. Response
Response payload is described under Response Fields above.
curl --location 'https://api.apillon.io/nfts/collections/substrate' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Basic :credentials' \ +--data '{ + "collectionType": 1, + "chain": 8, + "symbol": "NFT", + "name": "NFT Collection", + "description": "NFT Collection description", + "maxSupply": 1000, + "baseUri": "https://ipfs.apillon.io/metadata/", + "baseExtension": "json", + "royaltiesAddress": "5TdmScYtgDGg13mG1pvQ5zAooMXMK45bHBt3meGDXrNBKua", + "royaltiesFees": 10, + "drop": true, + "dropStart": 1687251003, + "dropReserve": 5, + "dropPrice": 0.1 +}' +
{ + "id": "b5935c73-204d-4365-9f9a-6a1792adab5b", + "status": 200, + "data": { + "createTime": "2023-06-13T10:15:58.000Z", + "updateTime": "2023-06-13T10:15:58.000Z", + "chain": 8, + "collectionType": 1, + "collectionUuid": "d6355fd3-640d-4803-a4d9-79d875abcb5a", + "symbol": "NFT", + "name": "NFT Collection", + "description": "NFT Collection Description", + "maxSupply": 1000, + "bucketUuid": "a9425ff7-4802-4a38-b771-84a790112c30", + "baseUri": "https://ipfs.apillon.io/metadata/", + "baseExtension": ".json", + "isSoulbound": false, + "isRevokable": false, + "royaltiesFees": 0.1, + "royaltiesAddress": "5TdmScYtgDGg13mG1pvQ5zAooMXMK45bHBt3meGDXrNBKua", + "collectionStatus": 0, + "contractAddress": "XjuXMnFxcJoAgMCdUQKvvt2Daykq4H9rsCfYEVpF6noFP5u", + "transactionHash": "0xb59d8497feb121b0ca0b8480df72a456333edddc68ad65f23b6b8b9028e3a6b3", + "deployerAddress": "WmMcyrPY4fivB5FUPN85QPhCMKtnrjmUyAgtXC2oW2XbcnY", + "drop": true, + "dropStart": 1687251003, + "dropPrice": 0.1, + "dropReserve": 5 + } +} +
Create EVM NFT Collection
POST /nfts/collections/evmBody fields
All the fields from substrate collection, plus:
Name Type Description Required chain number
Blockchain ID on which you want to release your collection. Options: ( 1284
- Moonbeam,1287
- Moonbase,592
- Astar)true isRevokable boolean
For revocable collection owner can destroy NFTs at any time. (default: false) true isSoulbound boolean
Soul bound tokens are NFTs that are bound to wallet and not transferable. (default: false) true isAutoIncrement boolean
If set to false, enables minting NFTs with a custom token ID, otherwise defaults to isAutoIncrement = true false Possible errors
Beside validation errors (with 422 http status code) these are the error codes may be returned:
Code Description 40012002 Collection quota reached 50012003 Failed deploying NFT contract on chain. 50012010 Failed to create bucket for storing metadata. Response
Response payload is described under Response Fields above.
curl --location 'https://api.apillon.io/nfts/collections/evm' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Basic :credentials' \ +--data '{ + "collectionType": 1, + "chain": 1287, + "symbol": "NFT", + "name": "NFT Collection", + "description": "NFT Collection description", + "maxSupply": 1000, + "baseUri": "https://ipfs.apillon.io/metadata/", + "baseExtension": "json", + "royaltiesAddress": "0x4156edbafc5091507de2dd2a53ded551a346f83b", + "royaltiesFees": 10, + "drop": true, + "dropStart": 1687251003, + "dropReserve": 5, + "dropPrice": 0.1, + "isRevokable": true, + "isSoulbound": true +}' +
{ + "id": "b5935c73-204d-4365-9f9a-6a1792adab5b", + "status": 200, + "data": { + "createTime": "2023-06-13T10:15:58.000Z", + "updateTime": "2023-06-13T10:15:58.000Z", + "chain": 1287, + "collectionType": 1, + "collectionUuid": "d6355fd3-640d-4803-a4d9-79d875abcb5a", + "symbol": "NFT", + "name": "NFT Collection", + "description": "NFT Collection Description", + "maxSupply": 1000, + "bucketUuid": "a9425ff7-4802-4a38-b771-84a790112c30", + "baseUri": "https://ipfs.apillon.io/metadata/", + "baseExtension": ".json", + "isSoulbound": false, + "isRevokable": false, + "royaltiesFees": 0.1, + "royaltiesAddress": "0x4156edbafc5091507de2dd2a53ded551a346f83b", + "collectionStatus": 0, + "contractAddress": "0x452101C96A1Cf2cBDfa5BB5353e4a7F235241557", + "transactionHash": "0x6b97424de3367cd0335b08265787b83053b62bee2d1c8bec1f776936bea4fb26", + "deployerAddress": "0x4156edbafc5091507de2dd2a53ded551a346f83b", + "drop": true, + "dropStart": 1687251003, + "dropPrice": 0.1, + "dropReserve": 5, + "isRevokable": true, + "isSoulbound": true + } +} +
Transfer Collection
Transfer collection ownership from a wallet owned by caller to a new wallet address.
POST/nfts/collections/:uuid/transferURL parameters
Name Description Required uuid Unique key of collection. Key is displayed in developer dashboard. true Body fields
Name Type Description Required address string
Wallet address of a new owner. true Possible errors
Beside validation errors (with 422 http status code) these are the error codes may be returned:
Code Description 40012003 Contract can't be transferred to wallet address that already owns this collection. 40012004 Transfer transaction already exists. 40300000 Not allowed to access collection 50012002 Collection doesn't exist, wasn't deployed or was already transferred. 50012004 Collection transfer failed. Response
Response payload is described under Response Fields above.
curl --location 'https://api.apillon.io/nfts/collections/:uuid/transfer' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Basic :credentials' \ +--data '{"address": "0x452101C96A1Cf2cBDfa5BB5353e4a7F235241551"}' +
{ + "id": "b5935c73-204d-4365-9f9a-6a1792adab5b", + "status": 200, + "data": { + "createTime": "2023-06-13T10:15:58.000Z", + "updateTime": "2023-06-13T10:15:58.000Z", + "collectionType": 1, + "collectionUuid": "d6355fd3-640d-4803-a4d9-79d875abcb5a", + "symbol": "NFT", + "name": "NFT Collection", + "description": "NFT Collection Description", + "maxSupply": 1000, + "bucketUuid": "a9425ff7-4802-4a38-b771-84a790112c30", + "baseUri": "https://ipfs.apillon.io/metadata/", + "baseExtension": ".json", + "isSoulbound": false, + "isRevokable": true, + "royaltiesFees": 0.1, + "royaltiesAddress": "0x4156edbafc5091507de2dd2a53ded551a346f83b", + "collectionStatus": 0, + "contractAddress": "0x452101C96A1Cf2cBDfa5BB5353e4a7F235241557", + "transactionHash": "0x6b97424de3367cd0335b08265787b83053b62bee2d1c8bec1f776936bea4fb26", + "deployerAddress": "0x4156edbafc5091507de2dd2a53ded551a346f83b", + "chain": 1287, + "drop": true, + "dropStart": 1687251003, + "dropPrice": 0.1, + "dropReserve": 5 + } +} +
Mint Collection NFTs
Mint specified amount of NFTs to a wallet address provided in request.
Note: if the collection is set as
drop
this endpoint can only mint reserved NFTs.POST/nfts/collections/:uuid/mintURL parameters
Name Description Required uuid Unique key of collection. Key is displayed in developer dashboard. true Body fields
Name Type Description Required receivingAddress string
Wallet address of NFT receiver. true quantity number
Number of NFTs to mint. true Possible errors
Beside validation errors (with 422 http status code) these are the error codes may be returned:
Code Description 40300000 Not allowed to access collection. 50012002 Collection doesn't exist, wasn't deployed or was already transferred. 50012005 Error minting NFT. 50012007 Total number of minted NFTs would exceed max supply for this collection. 50012008 All of the reserved NFTs were already minted. Response Fields
Field Type Description success boolean
Status of action. curl --location 'https://api.apillon.io/nfts/collections/:uuid/mint' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Basic :credentials' \ +--data '{"receivingAddress": "0x452101C96A1Cf2cBDfa5BB5353e4a7F235241557", "quantity": 1}' +
{ + "id": "b5935c73-204d-4365-9f9a-6a1792adab5b", + "status": 200, + "data": { + "success": true + } +} +
Nest Mint Collection NFTs
Nest mint specified amount of NFTs under a parent NFT defined by the parent collection UUID and token id.
POST/nfts/collections/:uuid/nest-mintURL parameters
Name Description Required uuid Unique key of (child) collection we are minting. true Body fields
Name Type Description Required parentCollectionUuid string
Collection UUID of NFT receiving nest-minted NFT. true parentNftId number
Token id of NFT receiving nest-minted NFT. true quantity number
Number of NFTs to nest-mint. true Possible errors
Beside validation errors (with 422 http status code) these are the error codes may be returned:
Code Description 40300000 Not allowed to access collection. 50012002 Collection doesn't exist, wasn't deployed or was already transferred. 50012007 Total number of minted NFTs would exceed max supply for this collection. 50012008 All of the reserved NFTs were already minted. 50012013 Parrent collection doesn't support nesting. 50012014 Parrent and child collection chain missmatch. Response Fields
Field Type Description success boolean
Status of action. curl --location 'https://api.apillon.io/nfts/collections/:uuid/next-mint' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Basic :credentials' \ +--data '{"parentCollectionUuid": "d6355fd3-640d-4803-a4d9-79d875abcb5a", "parentNftId": 1, "quantity": 1}' +
{ + "id": "b5935c73-204d-4365-9f9a-6a1792adab5b", + "status": 200, + "data": { + "success": true + } +} +
Burn Collection NFT
Burn specific NFT belonging to collection specified.
Note: burning NFTs is only available if
isRevokable
is enabled on collection.POST/nfts/collections/:uuid/burnURL parameters
Name Description Required uuid Unique key of collection. Key is displayed in developer dashboard. true Body fields
Name Type Description Required tokenId number
Non fungible token id that we are burning. true Possible errors
Beside validation errors (with 422 http status code) these are the error codes may be returned:
Code Description 40300000 Not allowed to access collection. 50012002 Collection doesn't exist, wasn't deployed or was already transferred. 50012012 Burning NFT failed. Response fields
Field Type Description success boolean
Status of action. curl --location 'https://api.apillon.io/nfts/collections/:uuid/burn' \ +--header 'Content-Type: application/json' \ +--header 'Authorization: Basic :credentials' \ +--data '{"tokenId": 1}' +
{ + "id": "b5935c73-204d-4365-9f9a-6a1792adab5b", + "status": 200, + "data": { + "status": true + } +} +
Apillon SDK | Apillon Wiki + + + + ++ + + diff --git a/build/6-apillon-cli.html b/build/6-apillon-cli.html new file mode 100644 index 00000000..ce6f7857 --- /dev/null +++ b/build/6-apillon-cli.html @@ -0,0 +1,313 @@ + + + + + + + + +Apillon Wiki Apillon SDK
Libraries and tools for interacting with your Apillon integration. Apillon SDK reduces the amount of work required to use Apillons REST API. It reduces the boilerplate code you need to write as well as compresses multi step flows into single operations.
Requirements
- npm 10.0.0 or higher
- node.js 20.0.0 or higher
- Apillon API key and secret
Getting started
To be able to use Apillon SDK, you must register an account at Apillon.io, create a project and generate an API key with appropriate permissions.
SDK package is available on NPM and you can also check it out directly on GitHub.
Installation
npm install @apillon/sdk +
Initialization
import { Hosting } from '@apillon/sdk'; + +const hosting = new Hosting({ + key: '', + secret: '', +}); +
Apillon SDK consists of different modules depending on which service you want to use. All modules require the same initial config of
key
andsecret
shown above inHosting
module example.Alternatively, you can populate the
APILLON_API_KEY
andAPILLON_API_SECRET
environment variables.View each individual module examples in the sections below.
Detailed docs
This wiki only contains the basic installation and examples of SDK usage. For additional information on using the SDK, see the Detailed SDK documentation.
Examples
Examples for using Apillon can be found in a demo repo here. Instructions on running the examples are in the README file.
TIP
You can run examples directly in your browser via CodeSandbox.
Hosting
Hosting module encapsulates functionalities for Hosting service available on Apillon dashboard.
TIP
You can only create a new webpage through the dashboard hosting service.
The flow of deploying a new website looks like this:
- Upload new website files
- Trigger deploy to staging
- Trigger deploy from staging to production
You can also directly deploy uploaded files to production.
For detailed hosting SDK method, class and property documentation visit SDK hosting docs.
Usage example
import { + DeployToEnvironment, + DeploymentStatus, + Hosting, + LogLevel, +} from '@apillon/sdk'; +import * as fs from 'fs'; + +const hosting = new Hosting({ + key: 'yourApiKey', + secret: 'yourApiSecret', + logLevel: LogLevel.VERBOSE, +}); + +// list all websites +await hosting.listWebsites({ orderBy: 'createTime' }); + +// create an instance of a website via uuid +const webpage1 = hosting.website('uuid'); + +// gets website information +await webpage1.get(); + +// Upload files from local folder +await webpage1.uploadFromFolder('./public'); +// Or alternatively, send file buffers as upload parameters +const htmlBuffer = fs.readFileSync('./public/index.html'); +await webpage1.uploadFiles( + [ + { + fileName: 'index.html', + contentType: 'text/html', + content: htmlBuffer, + }, + ] +); + +// deploys uploaded files to staging environment +await webpage1.deploy(DeployToEnvironment.TO_STAGING); + +// lists all deployments of a website +await webpage1.listDeployments(); + +// gets a specific deployment +const deployment = await webpage1 + .deployment('3e0c66ea-317d-4e1f-bcd9-38026c3ea1ee') + .get(); + +// checks if deployment was successful +if (deployment.deploymentStatus === DeploymentStatus.SUCCESSFUL) { + // done +} +
Storage
Storage module encapsulates functionalities for Storage service available on Apillon dashboard.
For detailed storage SDK method, class and property documentation visit SDK storage docs.
Usage example
import { Storage, LogLevel, FileStatus } from '@apillon/sdk'; +import * as fs from 'fs'; + +const storage = new Storage({ + key: 'yourApiKey', + secret: 'yourApiSecret', + logLevel: LogLevel.VERBOSE, +}); + +// list buckets +await storage.listBuckets({ limit: 5 }); + +// create and instance of a bucket directly through uuid +const bucket = storage.bucket('uuid'); + +// Upload files from local folder +await bucket.uploadFromFolder('./my-folder/files/'); +// Or alternatively, send file buffers as upload parameters +const pdfBuffer = fs.readFileSync('./my-folder/files/document.pdf'); +await bucket.uploadFiles( + [ + { + fileName: 'document.pdf', + contentType: 'application/pdf', + content: pdfBuffer, + }, + ], + // Upload the files in a new subdirectory in the bucket instead of in the root of the bucket + { wrapWithDirectory: true, directoryPath: 'main/documents' } +); + +// list objects (files, folders) in a bucket +await bucket.listObjects({ + directoryUuid: 'eaff2672-3012-46fb-9278-5efacc6cb616', + markedForDeletion: false, + limit: 5, +}); + +// list all files in a bucket no matter if they are in a folder or not +await bucket.listFiles({ fileStatus: FileStatus.UPLOADED }); + +// generate an IPFS link for a CID +const cid = 'bafybeigjhyc2tpvqfqsuvf3byo4e4a4v6spi6jk4qqvvtlpca6rsaf2cqi'; +const link = await storage.generateIpfsLink(cid); + +// gets a specific file in a bucket directly through uuid +const file = await bucket.file('2195521d-15cc-4f6e-abf2-13866f9c6e03').get(); + +// deletes a file via uuid +await bucket.file('2195521d-15cc-4f6e-abf2-13866f9c6e03').delete(); +// deletes a directory via uuid +await bucket.directory('eddc52cf-92d2-436e-b6de-42d7cad621c3').delete(); +
IPNS methods
The Storage module additionally contains methods for manipulating IPNS records for a specific storage any.
For detailed IPNS SDK method, class and property documentation visit SDK IPNS docs.
import { Storage, LogLevel } from '@apillon/sdk'; + +const storage = new Storage({ + key: 'yourApiKey', + secret: 'yourApiSecret', + logLevel: LogLevel.VERBOSE, +}); + +// create and instance of a bucket directly through uuid +const bucket = storage.bucket('uuid'); +// list all existing IPNS records +const ipnsNames = await bucket.listIpnsNames({ ipnsName: 'Images IPNS' }); +// create a new IPNS record +const newIpns = await bucket.createIpns({ + name: 'Music IPNS', + description: 'IPNS for my music files', + cid: 'QmS5NL2Rc6SCjFx7pvZHdTD8WGWjDt25WQskC7DsNKAatW', +}); +// Get an IPNS record's details by UUID +const ipns = await bucket.ipns('ipns_uuid').get(); +// Publish an IPNS record to point to a given CID +await ipns.publish('QmajaeC15ZpcnjBpX4ARRBU127fpcZ2svYEfEBhFRkRZbN'); +// delete an IPNS record from the bucket +await ipns.delete(); +
NFTs
NFT module encapsulates functionalities for NFT service available on Apillon dashboard.
For detailed NFT SDK method, class and property documentation visit SDK NFT docs.
Warning When you transfer ownership of the collection to another account Apillon will lose the ability to perform actions in your name (mint, burn, etc.). Before you transfer ownership make sure you do not need those functionalities via Apillon anymore.
Usage example
import { + CollectionType, + EvmChain, + LogLevel, + Nft, + TransactionStatus, +} from '@apillon/sdk'; + +const nft = new Nft({ + key: 'yourApiKey', + secret: 'yourApiSecret', + logLevel: LogLevel.VERBOSE, +}); + +// create a new collection +let collection = await nft.create({ + collectionType: CollectionType.GENERIC, + chain: EvmChain.MOONBEAM, + name: 'SpaceExplorers', + symbol: 'SE', + description: 'A collection of unique space exploration NFTs.', + baseUri: 'https://moonbeamnfts.com/collections/spaceexplorers/', + baseExtension: 'json', + // If you omit the maxSupply parameter, the max supply will be unlimited + maxSupply: 1000, + isRevokable: false, + isSoulbound: false, + royaltiesAddress: '0x1234567890abcdef', + royaltiesFees: 5, + drop: true, + dropStart: 1679875200, + dropPrice: 0.05, + dropReserve: 100, +}); +// or create a substrate collection +const substrateCollection = await nft.createSubstrate({ + collectionType: CollectionType.GENERIC, + chain: SubstrateChain.ASTAR, + name: 'SpaceExplorers', + symbol: 'SE', + ... +}); + +// check if collection is deployed - available on chain +if (collection.collectionStatus == CollectionStatus.DEPLOYED) { + console.log('Collection deployed: ', collection.transactionHash); +} + +// search through collections +await nft.listCollections({ search: 'My NFT' }); + +// create and instance of collection directly through uuid +collection = await nft.collection('uuid').get(); + +// mint a new nft in the collection +await collection.mint({ + receivingAddress: '0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD', + quantity: 1, +}); + +// nest mint a new nft if collection type is NESTABLE +await collection.nestMint(collection.uuid, 1, 1); + +// burn/destroy a specific NFT by its ID if collection is set as revokable +await collection.burn('1'); + +// list confirmed transactions on a collection +await collection.listTransactions({ + transactionStatus: TransactionStatus.CONFIRMED, +}); + +// transfer ownership of a collection away from apillon platform to an address +// NOTE that this will disable the ability to mint/burn etc. from the SDK/API since only the owner +// has this ability +await collection.transferOwnership( + '0x5BA8B0c24bA5307b67E619ad500a635204F73bF1' +); +
Identity
Identity module encapsulates functionalities for validating EVM and Polkadot wallet signatures, as well as fetching Polkadot Identity data for any wallet.
For detailed hosting SDK method, class and property documentation visit SDK identity docs.
Usage example
import { Identity, LogLevel } from '@apillon/sdk'; + +// Note: for signature-related methods API config is not required +const identity = new Identity({ + key: 'yourApiKey', + secret: 'yourApiSecret', + logLevel: LogLevel.VERBOSE, +}); + +// obtain on-chain identity data for a Polkadot wallet +const { polkadot, subsocial } = await identity.getWalletIdentity(address); + +async function validateEvmWalletSignature() { + // generate a custom message to be signed by the user's wallet + const { message, timestamp } = await identity.generateSigningMessage( + 'Custom display message here', + ); + + // alternatively, you can generate you own signing message with timestamp like this: + // const timestamp = new Date().getTime(); + // const message = 'Message from my Dapp'; + // const signingMessage = `${message}\n${timestamp}`; + + const walletAddress = '0xa79bg13g2...'; + + // validate an EVM wallet's signature for a given message + const { isValid, address } = await identity.validateEvmWalletSignature({ + message, + signature, // signature obtained from the user's wallet by the client app + walletAddress, + /* + * optional - check signature time validity by providing a timestamp + * which indicates when the signature was generated + */ + timestamp, + // additionally, specify for how many minutes the timestamp is valid + signatureValidityMinutes: 15, + }); + + console.log(isValid); // true + console.log(address.toLowerCase() === walletAddress.toLowerCase()); // true +} + +async function validatePolkadotWalletSignature() { + // If you wish to generate the message yourself and validate the timestamp, + // use a signing message as shown below: + const timestamp = new Date().getTime(); + const message = 'Message from my Dapp'; + const signingMessage = `${message}\n${timestamp}`; + + // validate a Polkadot wallet's signature for a given signing message + const { isValid } = await identity.validatePolkadotWalletSignature({ + message: signingMessage, + signature, // signature obtained from the user's wallet by the client app + walletAddress: '5HqHQDGcHqS...', + timestamp, + signatureValidityMinutes: 5, + }); +} + +
Computing
The Computing module provides functionalities for managing computing contracts, including creating contracts, listing contracts, and interacting with specific contracts for operations like encryption and ownership transfer.
Usage example
import { Computing } from '@apillon/sdk'; + +const computing = new Computing({ + key: 'yourApiKey', + secret: 'yourApiSecret', +}); + +// List all computing contracts +const contracts = await computing.listContracts(); + +// Create a new computing contract +const newContract = await computing.createContract({ + name: 'New Contract', + description: 'Description of the new contract', + bucket_uuid, + contractData: { + nftContractAddress: '0xabc...', + nftChainRpcUrl: ChainRpcUrl.ASTAR, + }, +}); + +// Interact with a specific computing contract +const contract = computing.contract(newContract.uuid); + +// Get details of the contract +const contractDetails = await contract.get(); + +// List transactions of the contract +const transactions = await contract.listTransactions(); + +// Encrypt a file and upload it to the associated bucket +const encryptionResult = await contract.encryptFile({ + fileName: 'example.txt', + content: Buffer.from('Hello, world!'), + nftId: 1, // NFT ID used for decryption authentication +}); + +// Transfer ownership of the contract +const newOwnerAddress = '0xNewOwnerAddress'; +const successResult = await contract.transferOwnership(newOwnerAddress); +console.log( + `Ownership transfer was ${successResult ? 'successful' : 'unsuccessful'}.`, +); +
Social
The Social module provides functionalities for managing social hubs and channels within the Apillon platform. This includes creating, listing, and interacting with hubs and channels. In the background it utilizes Grill.chat, a mobile-friendly, anonymous chat application powered by Subsocial.
Usage example
import { Social } from '@apillon/sdk'; + +const social = new Social({ key: 'yourApiKey', secret: 'yourApiSecret' }); +// Create a new hub +const hub = await social.createHub({ + name: 'Apillon Hub', + about: 'Hub for Apillon channels', + tags: 'apillon,web3,build', +}); + +// Get a specific hub by UUID +const hubDetails = await social.hub(hub.uuid).get(); +// List all Hubs +const hubs = await social.listHubs(); + +// Create a new channel within a hub +const channel = await social.createChannel({ + title: 'Web3 Channel', + body: "Let's discuss Web3", + tags: 'web3,crypto', + hubUuid: hub.uuid, +}); + +// Get a specific channel by UUID +const channelDetails = await social.channel(channel.uuid).get(); +// List all channels within a Hub +const channels = await social.listChannels({ hubUuid: hub.uuid }); +
Apillon CLI | Apillon Wiki + + + + ++ + + diff --git a/build/7-apillon-oauth-integration.html b/build/7-apillon-oauth-integration.html new file mode 100644 index 00000000..28f485ee --- /dev/null +++ b/build/7-apillon-oauth-integration.html @@ -0,0 +1,76 @@ + + + + + + + + +Apillon Wiki Apillon CLI
Apillon CLI is a command-line interface for using Apillon Web3 services.
Requirements
To be able to use Apillon CLI, you must register an account at Apillon.io, create a project and generate an API key with appropriate permissions. Also Node.js (version 16 or later) is required.
Installation
To install Apillon CLI run
npm install -g @apillon/cli +
Afterwards you can use CLI with command
apillon <command> [options] +
Alternately you don't need to install the Apillon CLI to use it. In that case run the desired command using
npx
:npx @apillon/cli <command> [options] +
Note that when running without installation, you have to use
@apillon/cli
instead ofapillon
execution command.Global Options
--key <api key>
: Apillon API key (can be set via theAPILLON_API_KEY
environment variable).--secret <api secret>
: Apillon API secret (can be set via theAPILLON_API_SECRET
environment variable).--debug
: Output execution logs when running commands.-V
,--version
: Output the version number.Environment Variables
You can use environment variables to set an API key, and an API secret:
APILLON_API_KEY
: Apillon API key.APILLON_API_SECRET
: Apillon API secret.If you have these variables set, you do not need to use the global options each time.
Help
To display the help information for the CLI or a specific command, use the
-h
or--help
option:apillon -h +apillon hosting -h +npx @apillon/cli hosting deploy-website --help +
List pagination options
For commands that return a list of results, for example
apillon storage list-files
, orapillon hosting list-websites
, there are global list pagination options that are available to use:
--limit <integer>
: Page limit--order-by <string>
: Page order by (can be any property from the response data)--page <integer>
: Page number--search <string>
: Search by name or other object identifierFor example responses and for an overview of all properties, refer to the Apillon API wiki. Note: CLI responses may be dfferent from API responses.
Commands
The Apillon CLI currently supports the following commands:
Hosting Commands
hosting list-websites
Lists all websites associated with your project.
Example
apillon hosting list-websites --search "My-Website" --limit 1 +
Example response
{ + "items": [ + { + "createTime": "2023-10-25T10:41:06.000Z", + "updateTime": "2023-10-26T12:41:41.000Z", + "uuid": "5b908779-3687-4592-a073-9bebbf86afe2", + "name": "My Website", + "description": "My own website", + "domain": "https://my-website.com", + "bucketUuid": "47251013-37c6-4b30-be2b-8583dea25c4c", + "ipnsStaging": "k2k4r8ob2rf35wbmhhtzbq6nd4lhwv...", + "ipnsProduction": "k2k4r8pple7phwm9azqgxshxdzy..." + }, + ... + ], + "total": 3 +} +
hosting get-website
Retrieves information about a specific website.
Options
--uuid <string>
: UUID of the website to get details for.Example
apillon hosting get-website --uuid "123e4567-e89b-12d3-a456-426655440000" +
hosting deploy-website
Deploys a website from a local folder directly to Apillon hosting production environment.
Options
<file-path>
: Path to the folder containing your website files.--uuid <string>
: UUID of the website to upload files to.-p, --preview
: Deploy to staging environment instead.Example
apillon hosting deploy-website ./public_html --uuid "123e4567-e89b-12d3-a456-426655440000" -p +
hosting upload
Uploads a local folder's contents to a website deployment bucket.
Options
<path>
: Path to the folder containing your website files.--uuid <string>
: UUID of the website to upload files to.Example
apillon hosting upload ./public_html --uuid "123e4567-e89b-12d3-a456-426655440000" +
hosting start-deployment
Deploys a website to the specified environment, from files already uploaded to the hosting bucket.
Options
--uuid <string>
: UUID of the website to deploy.--env <integer>
: The environment to deploy to.Available choices:
TO_STAGING = 1 +STAGING_TO_PRODUCTION = 2 +DIRECTLY_TO_PRODUCTION = 3 +
Example
apillon hosting start-deployment --uuid "123e4567-e89b-12d3-a456-426655440000" --env 1 +
hosting list-deployments
Lists all deployments for a specific website.
Options
--uuid <string>
: UUID of the website to list deployments for.--status <integer>
: The status of the deployments (DeploymentStatus enum, optional).Available choices:
INITIATED = 0 +IN_PROGRESS = 1 +IN_REVIEW = 2 +APPROVED = 3 +SUCCESSFUL = 10 +FAILED = 100 +REJECTED = 101 +
--env <integer>
: The environment of the deployments (DeploymentStatus enum, optional).Available choices:
STAGING = 2 +PRODUCTION = 3 +
Example
apillon hosting list-deployments --uuid "58a16026-1356-405b-97f9-efcc9dfac1dd" --order-by createTime --desc true +
Example response
{ + "items": [ + { + "createTime": "2023-11-14T12:09:20.000Z", + "updateTime": "2023-11-14T12:09:42.000Z", + "uuid": "9b677fe2-1bb1-44d9-8956-e7749452f02d", + "websiteUuid": "58a16026-1356-405b-97f9-efcc9dfac1dd", + "cid": "QmPPBMsFccJVaLwvdhSh3zMbfEvonxoNSBLVd1kWK34Nps", + "cidv1": "bafybeizpqaa5xb5r46d2voj35qtokhb3c3bekof...", + "environment": "DIRECTLY_TO_PRODUCTION", + "deploymentStatus": "SUCCESSFUL", + "size": 7162, + "number": 1 + }, + ... + ], + "total": 7 +} +
hosting get-deployment
Retrieves information about a specific deployment.
Options
-w, --website-uuid <string>
: UUID of the website.-d, --deployment-uuid <string>
: UUID of the deploymentExample
apillon hosting get-deployment --website-uuid "123e4567-e89b-12d3-a456-426655440000" --deployment-uuid "987e6543-e21c-32f1-b123-426655441111" +
Storage Commands
storage list-buckets
Lists all storage buckets associated with your project.
Example
apillon storage list-buckets +
Example response
{ + "items": [ + { + "createTime": "2023-11-15T09:56:53.000Z", + "updateTime": "2023-11-23T08:55:46.000Z", + "uuid": "91c57d55-e8e4-40b7-ad6a-81a82831bfb3", + "name": "My Storage Bucket", + "description": "For storing my images and videos", + "size": 23576 + }, + ... + ], + "total": 2 +} +
storage list-objects
Retrieves objects (files and folders) from a specific bucket or bucket directory.
Options
-b, --bucket-uuid <string>
: UUID of the bucket to retrieve objects from.-d, --directory-uuid <string>
: UUID of the directory to retrieve objects from (optional, default root folder).--deleted
: Include objects deleted from the bucket.Example
apillon storage list-objects --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --directory-uuid "987e6543-e21c-32f1-b123-426655441111" +
Example response
{ + "items": [ + { + "createTime": "2023-11-23T08:55:45.000Z", + "updateTime": "2023-11-23T08:55:46.000Z", + "uuid": "14a7a891-877c-41ac-900c-7382347e1e77", + "name": "index.html", + "CID": "bafybeidzrd7p5ddj67j2mud32cbnze2c7b2pvbhn...", + "status": "AVAILABLE_ON_IPFS_AND_REPLICATED", + "directoryUuid": null, + "type": "FILE", + "link": "https://ipfs.apillon.io/ipfs/bafybeidzrd7p5ddj67j...", + "path": null, + "bucketUuid": "91c57d55-e8e4-40b7-ad6a-81a82831bfb3" + }, + ... + ], + "total": 16 +} +
storage list-files
Retrieves files from a specific bucket.
Options
-b, --bucket-uuid <string>
: UUID of the bucket to retrieve files from.-s, --file-status <integer>
: Filter by file status (FileStatus enum, optional).Available choices:
UPLOAD_REQUEST_GENERATED = 1 +UPLOADED = 2 +AVAILABLE_ON_IPFS = 3 +AVAILABLE_ON_IPFS_AND_REPLICATED = 4 +
Example
apillon storage list-files --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" -s 2 +
Example response
{ + "items": [ + { + "createTime": "2023-11-15T09:58:04.000Z", + "updateTime": "2023-11-15T09:58:10.000Z", + "name": "style.css", + "CID": "bafybeidzrd7p5ddj67j2mud32cbnze2c7b2pvbhn...", + "status": "AVAILABLE_ON_IPFS_AND_REPLICATED", + "directoryUuid": null, + "type": "FILE", + "link": "https://ipfs.apillon.io/ipfs/bafybeidzrd7p...", + "path": null, + "bucketUuid": "91c57d55-e8e4-40b7-ad6a-81a82831bfb3" + }, + ... + ], + "total": 10 +} +
storage upload
Upload contents of a local folder to specified bucket.
Options
<folder-path>
: Path to the folder containing your files.-b, --bucket-uuid <string>
: UUID of the bucket to upload files to.-w, --wrap
: Wrap uploaded files to an IPFS directory-p, --path <string>
: Path to upload files to (e.g. main/subdir). Required when --wrap is supplied.--await
: await file CIDs to be resolvedExample
apillon storage upload ./my_folder --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --wrap --path "main/subdir" +
storage get-file
Retrieves information about a specific file in a bucket.
Options
-b, --bucket-uuid <string>
: UUID of the bucket.-f, --file-uuid <string>
: UUID or CID of the file to retrieve.Example
apillon storage get-file --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --file-uuid "file_uuid_or_cid" +
storage delete-file
Deletes a specific file from a bucket.
Options
-b, --bucket-uuid <string>
: UUID of the bucket.-f, --file-uuid <string>
: UUID or CID of the file to delete.Example
apillon storage delete-file --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --file-uuid "file_uuid_or_cid" +
storage delete-directory
Delete a directory from a storage bucket.
Options
-b, --bucket-uuid <string>
: UUID of the bucket.-d, --directory-uuid <string>
: UUID of the directoru to delete.Example
apillon storage delete-directory --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --directory-uuid "2c84048c-49a1-4ed2-9e1e-8920806ae968" +
IPNS Commands
storage ipns list
Lists all IPNS records for a specific bucket.
Options
-b, --bucket-uuid <uuid>
: UUID of the bucket.Example
apillon storage ipns list --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" +
Example response
{ + "items": [ + { + "createTime": "2023-12-06T12:29:09.000Z", + "updateTime": "2023-12-06T12:29:21.000Z", + "uuid": "80383a54-1d86-4761-a5e4-26a2fab474c9", + "name": "Images IPNS", + "description": "IPNS for images folder", + "ipnsName": "k2k4r8jp1jnlbe3qv...", + "ipnsValue": "/ipfs/QmUz4...", + "link": "https://ipfs.apillon.io/ipns/k2k4r8jp1jnlbe3qv...", + "bucketUuid": "a26184d7-acf5-4d6c-9195-465e3a7a5240" + }, + { + "createTime": "2023-12-06T12:29:37.000Z", + "updateTime": "2023-12-06T12:29:52.000Z", + "uuid": "2045db5b-b347-4ea6-a4c0-4445e071180d", + "name": "JSON IPNS", + "description": "IPNS for metadata folder", + "ipnsName": "k2k4r8opkl3i2zq7bin8lis4...", + "ipnsValue": "/ipfs/QmUz5Z6RcMynfZWoC...", + "link": "https://ipfs.apillon.io/ipns/k2k4r8opkl3i2z...", + "bucketUuid": "a26184d7-acf5-4d6c-9195-465e3a7a5240" + } + ], + "total": 2 +} +
storage ipns create
Creates a new IPNS record for a specific bucket.
Options
-b, --bucket-uuid <uuid>
: UUID of the bucket.-n, --name <name>
: Name of the IPNS record.-d, --description <description>
: Description of the IPNS record (optional).-c, --cid <cid>
: CID to which this IPNS name will point.Example
apillon storage ipns create --bucket-uuid "123e4567-e89b-12d3-a456-426655440000" --name "my-ipns-record" --cid "QmWX5CcNvnaVmgGBn4o82XW9uW1uLvsHQDdNrANrQeSdXm" +
storage ipns get
Retrieves information about a specific IPNS record.
Options
-b, --bucket-uuid <uuid>
: UUID of the bucket.-i, --ipns-uuid <uuid>
: UUID of the IPNS record.Example
apillon storage ipns get --ipns-uuid "123e4567-e89b-12d3-a456-426655440000" +
storage ipns publish
Publishes an IPNS record to IPFS and links it to a CID.
Options
-b, --bucket-uuid <uuid>
: UUID of the bucket.-i, --ipns-uuid <uuid>
: UUID of the IPNS record.-c, --cid <string>
: CID to which this IPNS name will point.Example
apillon storage ipns publish --ipns-uuid "123e4567-e89b-12d3-a456-426655440000" --cid "QmWX5CcNvnaVmgGBn4o82XW9uW1uLvsHQDdNrANrQeSdXm" +
storage ipns delete
Deletes an IPNS record from a specific bucket.
Options
-b, --bucket-uuid <uuid>
: UUID of the bucket.-i, --ipns-uuid <uuid>
: UUID of the IPNS record.Example
apillon storage ipns delete --ipns-uuid "123e4567-e89b-12d3-a456-426655440000" +
NFT Commands
nfts list-collections
Lists all NFT collections owned by the project related to the API key.
Options
--status <integer>
: UUID of the collection to retrieve (CollectionStatus enum, optional).Available choices:
CREATED = 0 +DEPLOY_INITIATED = 1 +DEPLOYING = 2 +DEPLOYED = 3 +TRANSFERRED = 4 +FAILED = 5 +
Example
apillon nfts list-collections --status 3 +
Example response
{ + "items": [ + { + "createTime": "2023-11-20T10:21:12.000Z", + "updateTime": "2023-11-20T14:12:33.000Z", + "uuid": "2cda3a9b-01b1-4b5e-9709-7087129d55d0", + "symbol": "SE", + "name": "SpaceExplorers", + "description": "A collection of unique space exploration NFTs.", + "collectionType": "GENERIC", + "maxSupply": 1000, + "baseUri": "https://moonbeamnfts.com/collections/spaceexplorers/", + "baseExtension": ".json", + "isSoulbound": false, + "isRevokable": false, + "drop": false, + "dropPrice": 0.05, + "dropStart": 1679875200, + "dropReserve": 100, + "royaltiesFees": 5, + "royaltiesAddress": "0xaz5Bh6E56c5d3B58c944542de2bF18E7F65eED82", + "collectionStatus": "TRANSFERRED", + "contractAddress": "0x4e22162A6d0c91a088Cb57A72aB976ccA2A96B25", + "transactionHash": null, + "deployerAddress": "0xba015fgc6d80378a9a95f1687e9960857593983b", + "chain": "MOONBASE" + } + ], + "total": 1 +} +
nfts get-collection
Retrieves information about a specific NFT collection.
Options
--uuid <collection-uuid>
: UUID of the collection to retrieve.Example
apillon nfts get-collection --uuid "123e4567-e89b-12d3-a456-426655440000" +
nfts create-collection
Creates a new NFT collection. The JSON file needs to have the property structure as type ICreateCollection for EVM and ICreateSubstrateCollection for Substrate collections. An example object can be also seen on the NFT SDK docs.
Options
<file-path>
: Path to the JSON data file for the new collection.Example
apillon nfts create-collection ./nft-data.json +
nfts mint-nft
Mints NFTs for a collection with a specific UUID.
Options
--uuid <collection-uuid>
: UUID of the collection to mint NFTs to.-a, --address <string>
: Address which will receive minted NFTs.-q --quantity <integer>
: Number of NFTs to mint. (default 1).Example
apillon nfts mint-nft --uuid "123e4567-e89b-12d3-a456-426655440000" --address "0xdAC17F958D2ee523a2206206994597C13D831ec7" --quantity 2 +
nfts nest-mint-nft
Nest mints NFT child collection to a parent collection with a specific UUID and parent NFT with id.
Options
-c, --parent-collection-uuid <collection-uuid>
: Parent collection UUID to which child NFTs will be minted to.-p, --parent-nft-id <string>
: Parent collection NFT id to which child NFTs will be minted to.-q, --quantity <integer>
: Number of child NFTs to mint (default 1).Example
apillon nfts nest-mint-nft --parent-collection-uuid "123e4567-e89b-12d3-a456-426655440000" --parent-nft-id 5 --quantity 2 +
nfts burn-nft
Burns an NFT for a collection with a specific UUID.
Options
--uuid <collection-uuid>
: Collection UUID.-t, --token-id <integer>
: NFT id which will be burned.Example
apillon nfts burn-nft --uuid "123e4567-e89b-12d3-a456-426655440000" --token-id 123 +
nfts transfer-collection
Transfers NFT collection ownership to a new wallet address.
Options
--uuid <collection-uuid>
: Collection UUID.-a, --address <string>
: Address which you want to transfer collection ownership to.Example
apillon nfts transfer-collection --uuid "123e4567-e89b-12d3-a456-426655440000" --address "0xdAC17F958D2ee523a2206206994597C13D831ec7" +
nfts list-transactions
Lists NFT transactions for a specific collection UUID.
Options
--uuid <collection-uuid>
: Collection UUID.--status <integer>
: Transaction status (TransactionStatus enum, optional).Available choices:
PENDING = 1 +CONFIRMED = 2 +FAILED = 3 +ERROR = 4 +
--type <integer>
: Transaction type (TransactionType enum, optional).Available choices:
DEPLOY_CONTRACT = 1 +TRANSFER_CONTRACT_OWNERSHIP = 2 +MINT_NFT = 3 +SET_COLLECTION_BASE_URI = 4 +BURN_NFT = 5 +NEST_MINT_NFT = 6 +
Example
apillon nfts list-transactions --uuid "123e4567-e89b-12d3-a456-426655440000" +
Example response
{ + "items": [ + { + "createTime": "2023-11-20T10:21:22.000Z", + "updateTime": "2023-11-20T10:23:31.000Z", + "chainId": "MOONBEAM", + "transactionType": "DEPLOY_CONTRACT", + "transactionStatus": "CONFIRMED", + "transactionHash": "0xab99e630f9475df92768b1e5d73f4..." + }, + { + "createTime": "2023-11-20T11:55:13.000Z", + "updateTime": "2023-11-20T11:57:31.000Z", + "chainId": "MOONBEAM", + "transactionType": "MINT_NFT", + "transactionStatus": "CONFIRMED", + "transactionHash": "0x1ecfeeaeddfa0a39fc2ae1ec755d27..." + }, + ... + ], + "total": 4 +} +
Using in CI/CD tools
CLI is particularly useful for CI/CD builds and pipelines.
Deploying websites
Here's an example of how you can use the CLI tool in a CI/CD tool like GitHub Actions:
name: Deploy Website + +on: + push: + branches: + - master + +jobs: + deploy: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: 16 + + - name: Create dist folder + run: mkdir -p dist + + - name: Copy files + run: | + cp *.html dist/ + cp -r images dist/ + cp -r style dist/ + cp -r js dist/ + + #### + ## if you are using a framework for building web app, you can replace previous two step with the + ## appropriate command for generating static webpage, like an example bellow. + ## Find the correct command in your framework documentation. You may need to to change the + ## name of the source folder in the last step (CLI call) + #### + + # - name: Build app + # run: npm run build + + - name: Deploy website + env: + APILLON_API_KEY: ${{ secrets.APILLON_API_KEY }} + APILLON_API_SECRET: ${{ secrets.APILLON_API_SECRET }} + WEBSITE_UUID: ${{ secrets.WEBSITE_UUID }} + run: npx --yes @apillon/cli hosting deploy-website ./dist --uuid $WEBSITE_UUID --key $APILLON_API_KEY --secret $APILLON_API_SECRET + # Or alternatively you can use the run configuration below + # run: | + # npm i -g @apillon/cli + # apillon hosting deploy-website ./dist --uuid $WEBSITE_UUID --key $APILLON_API_KEY --secret $APILLON_API_SECRET +
In this example, the GitHub Actions workflow is triggered when a push event occurs on the master branch. The workflow performs the following steps:
- Checks out the repository.
- Sets up Node.js with version 16.
- Creates a dist folder to store the website files.
- Copies the necessary files (HTML, images, styles, and JavaScript) to the dist folder.
- Deploys the website using the CLI tool. The required environment variables (APILLON_API_KEY, APILLON_API_SECRET, and WEBSITE_UUID) are provided as secrets. The npx command ensures that the latest version of the CLI tool is used.
Make sure to setup secret variables with the values from Apillon platform.
That's it! You can now use this example as a starting point to deploy your website using the CLI tool in a CI/CD pipeline with GitHub Actions.
You can also check a working example on Github
Apillon OAuth Integration | Apillon Wiki + + + + ++ + + diff --git a/build/8-computing-api.html b/build/8-computing-api.html new file mode 100644 index 00000000..d4fac0f5 --- /dev/null +++ b/build/8-computing-api.html @@ -0,0 +1,181 @@ + + + + + + + + +Apillon Wiki Apillon OAuth Integration
You can easily integrate Apillon's OAuth protocol into your own project or website. If you don't have an Apillon account or project already, get started on the Apillon dashboard.
Once you have created a project, navigate to the project's API keys section in your project settings. Generate an API key for the Authentication service with the KEY_EXECUTE permission. This API key will be used to interact with Apillon's API, generate an OAuth session, and verify a user log-in. Make sure you store your API key and your API key secret in a secure manner.
To integrate Apillon's OAuth protocol into your website, follow these steps:
Create an Apillon Account: If you don't have an Apillon account or project yet, create one on the Apillon dashboard.
Generate an API Key: Go to your project's settings and navigate to the API keys section. Generate an API key for the Authentication service with the KEY_EXECUTE permission.
Securely Store API Key: It's crucial to securely store your API key and its secret. These will be used to interact with Apillon's API, create OAuth sessions, and verify user log-ins. This should be done on the server side, as explained below.
For a complete NodeJS demo of the whole OAuth flow, refer to this Github repo
Client - OAuth popup & events
To initiate the OAuth flow for the user, use the following code to open Apillon's OAuth website as a pop-up and prompt your users to complete the OAuth flow. The session token passed as a query parameter is obtained from the Apillon API (see Server section below).
Additionally, an event listener is added for the main app/website to handle the successful completion of the OAuth flow by the user, which grants an authentication token to the user. The token is then used to verify the login through Apillon's API, which finally returns the user's email address on Apillon.
async function openOAuthPopup() { + const sessionToken = await getAuthToken(); + oAuthWindow = window.open( + `https://oauth.apillon.io/?embedded=1&token=${sessionToken}`, + 'Apillon OAuth Form', + `height=${900} width=${450} resizable=no` + ); +} + +window.addEventListener('message', async event => { + if (!event.origin?.includes('apillon.io')) return; + + if (!event.data.verified) { + throw new Error('Invalid OAuth verification'); + } + // Close OAuth popup window + oAuthWindow?.close(); + + verifyUserLogin(event.data.authToken); +}, false); +
Server - Auth API endpoints
The server-side part of the OAuth implementation contains the logic to query the Apillon API, obtain a session token to initiate the OAuth flow, and verify the user log-in when the flow is finished.
Obtain a session token
Obtain a session token from the Apillon API to interact with Apillon's OAuth protocol.
GET /auth/session-tokencurl --location --request GET "https://api.apillon.io/auth/session-token" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +
{ + "id": "0da29b5a-8a8b-473b-9f97-3183819263f4", + "status": 200, + "data": { + "sessionToken": "eyJhbGciOiJIUzI1..." + } +} +
Verify user login
After the user has completed the OAuth flow, verify they have successfully logged in with the generated OAuth token from the "message" event handler. As a response, receive the user's Apillon email address.
POST /auth/verify-logincurl --location --request POST "https://api.apillon.io/auth/verify-login" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +--data-raw "{ + \"token\": \"eyJhbGciOiJIUzI1...\" +}" +
{ + "id": "de2cf1e7-0dfe-4378-ab77-98cbc9a00496", + "status": 200, + "data": { + "email": "apillon-user@mail.com" + } +} +
Computing API | Apillon Wiki + + + + ++ + + diff --git a/build/9-social-api.html b/build/9-social-api.html new file mode 100644 index 00000000..30b0b3c7 --- /dev/null +++ b/build/9-social-api.html @@ -0,0 +1,164 @@ + + + + + + + + +Apillon Wiki Computing API
The Computing Contracts API provides functionality for managing computing contracts, including creation, listing, encryption, ownership transfer, and assigning content identifiers (CIDs) to NFTs.
Create Computing Contract
Create a new computing contract with specified details.
POST /computing/contractsBody fields
Field Type Description Required contractType number
Type of the computing contract. Available options: 1 = SCHRODINGER Yes name string
Name of the computing contract Yes description string
Description of the computing contract No bucket_uuid string
UUID of the bucket for storing encrypted files from the computing contract. If this is not provided, a new bucket will be created No Data specific for Schrodinger's NFT contract type
Field Type Description Required contractData.nftContractAddress string
Contract address of the NFT collection whose tokens will be used for file decryption No contractData.nftChainRpcUrl string
RPC URL of the blockchain the NFT contract resides on Yes contractData.restrictToOwner boolean
If true, only the owner can encrypt files via the contract (Default: true) Yes Possible Errors
Code Description 40412003 Bucket from given bucket_uuid
param not found42200202 Contract type not present 42200203 Contract type not valid 42200204 Contract name not present 42200205 Contract name not valid (length in range 1-255) 42200206 Contract description not valid (length in range 1-1000) 42200210 Computing field not present 42200211 Computing contract data not valid 50012003 Error deploying contract Response
A response is an instance of the newly created contract.
curl --location --request POST "https://api.apillon.io/computing/contracts" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +--data-raw "{ + \"name\": \"My Computing Contract\", + \"description\": \"This contract is used for encrypting files associated with NFTs.\", + \"bucket_uuid\": \"def456...\", + \"contractType\": 1, + \"contractData\": { + \"nftContractAddress\": \"0x123456789abcdef0123456789abcdef0123456789\", + \"nftChainRpcUrl\": \"https://rpc.api.moonbeam.network/\", + \"restrictToOwner\": false + } +}" +
{ + "id": "0ac0bb74-5d78-4116-9e4f-1c3e8e92f032", + "status": 201, + "data": { + "createTime": "2024-02-20T13:59:06.290Z", + "updateTime": "2024-02-20T13:59:06.289Z", + "contractUuid": "defa1296-99f4-40de-8470-a2e8497e15ad", + "bucketUuid": "bf5940dc-673e-46bf-9207-e280c00d4f6b", + "name": "My Computing Contract", + "description": "This contract is used for encrypting files associated with NFTs.", + "contractType": 1, + "contractStatus": 1, + "contractAddress": null, + "deployerAddress": "44h63RRAv5PPjFJVp11uSa6v...", + "transactionHash": null, + "data": { + "nftContractAddress": "0xB601A99a1D1...", + "nftChainRpcUrl": "https://rpc.api.moonbeam.network/", + "restrictToOwner": false, + "ipfsGatewayUrl": "https://ipfs.nectarnode.io/ipfs", + "clusterId": "0x0000000000000000000000000000000000000000000000000000000000000001" + } + } +} +
List Computing Contracts
List all computing contracts with optional filters.
GET /computing/contractsQuery Parameters
All query parameters from listing request, plus:
Field Type Description Required contractStatus number
0
(created),1
(deploy initiated),2
(deploying),3
(deployed),4
(transferring),5
(transferred),6
(failed)No Response
A list of computing contracts that match the query parameters. Each entry in the list contains details about the contract.
curl --location --request GET "https://api.apillon.io/computing/contracts?project_uuid=abc123&contract_uuid=def456&contractStatus=1" \ +--header "Authorization: Basic :credentials" +
{ + "id": "14cf1522-bfa2-4190-972a-918992102ec2", + "status": 200, + "data": { + "items": [ + { + "createTime": "2024-02-20T13:58:45.000Z", + "updateTime": "2024-02-20T14:01:07.000Z", + "contractUuid": "defa1296-99f4-40de-8470-a2e8497e15ad", + "projectUuid": "4e913623-247b-4000-b650-8272430a3970", + "bucketUuid": "bf5940dc-673e-46bf-9207-e280c00d4f6b", + "name": "My Computing Contract", + "description": "This contract is used for encrypting files associated with NFTs.", + "contractType": 1, + "contractStatus": 3, + "contractAbiId": 1, + "contractAddress": "0xbdec1edc9b45a3...", + "deployerAddress": "44h63RRAv5PPjFJVp11uSa6v...", + } + ], + "total": 1, + "page": 1, + "limit": 20 + } +} +
Get Computing Contract
Retrieve details of a specific computing contract by UUID.
GET /computing/contracts/:uuidURL Parameters
Field Type Description Required uuid string
The UUID of the computing contract Yes Response fields (Computing Contract)
Based on your request to generate a table for the response fields that matches the structure seen in the bucket API documentation, here is how you can format the response fields for the "Get Computing Contract" section:
Field Type Description contractUuid
string
The unique identifier of the computing contract. bucketUuid
string
The UUID of the bucket associated with this computing contract, where encrypted files are stored. name
string
The name of the computing contract. description
string
A description of what the computing contract is used for. contractType
number
The type of computing contract. For example, 1
could represent SCHRODINGER.contractStatus
number
The current status of the computing contract. Possible values: 0
(created),1
(deploy initiated),2
(deploying),3
(deployed),4
(transferring),5
(transferred),6
(failed)contractAddress
string
The blockchain address of the computing contract. This can be null
if not yet deployed.deployerAddress
string
The address of the entity that deployed the computing contract. transactionHash
string
The transaction hash of the contract deployment. This can be null
if not yet deployed.data
object
An object containing additional data related to the computing contract. data.nftContractAddress
string
The contract address of the NFT collection used for file decryption. data.nftChainRpcUrl
string
The RPC URL of the blockchain where the NFT contract resides. data.restrictToOwner
boolean
Indicates if only the owner can encrypt and decrypt files via the contract. data.ipfsGatewayUrl
string
The URL of the IPFS gateway used by the computing contract. data.clusterId
string
An identifier for the cluster used by the computing contract. curl --location --request GET "https://api.apillon.io/computing/contracts/defa1296-99f4-40de-8470-a2e8497e15ad" \ +--header "Authorization: Basic :credentials" +
{ + "status": 200, + "data": { + "createTime": "2024-02-20T13:58:45.000Z", + "updateTime": "2024-02-20T14:01:07.000Z", + "contractUuid": "defa1296-99f4-40de-8470-a2e8497e15ad", + "bucketUuid": "bf5940dc-673e-46bf-9207-e280c00d4f6b", + "name": "My Computing Contract", + "description": "This contract is used for encrypting files associated with NFTs.", + "contractType": 1, + "contractStatus": 3, + "contractAddress": "0xbdec1edc9b45a31e1ebd38b1d1e464bbbddeb4fcd2687a39928029306039e48a", + "deployerAddress": "44h63RRAv5PPjFJVp11uSa6v52wRLmW7S5fiMWAyp3tNLh82", + "transactionHash": "0x4ea088f466eb08dc27d9d079cba1c5a35aeb1b5ff342b73bd456b9625dcd7093", + "data": { + "clusterId": "0x0000000000000000000000000000000000000000000000000000000000000001", + "ipfsGatewayUrl": "https://ipfs.nectarnode.io/ipfs", + "nftChainRpcUrl": "https://rpc.api.moonbeam.network/", + "restrictToOwner": false, + "nftContractAddress": "0xB601A99a1D1804c13780F9e53d661d8bEe6D3bF0" + } + } +} +
List Contract Transactions
List transactions for a specific computing contract.
GET /computing/contracts/:uuid/transactionsURL Parameters
Field Type Description Required uuid string
The UUID of the computing contract Yes Query Parameters
All query parameters from listing request, plus:
Field Type Description Required transactionType
number
The type of transaction. Possible values: 1
(deploy contract),2
(transfer contract ownership),3
(deposit to contract cluster),4
(assign cid to nft)transactionStatus
number
The current status of the transaction. Possible values: 1
(pending),2
(confirmed),3
(failed),4
(error),5
(worker success),6
(worker failed)Response fields
Field Type Description status
number
The status code of the transaction. walletAddress
string
The wallet address which initated the transaction. transactionType
number
The type of transaction. Possible values: 1
(deploy contract),2
(transfer contract ownership),3
(deposit to contract cluster),4
(assign cid to nft)transactionStatus
number
The current status of the transaction. Possible values: 1
(pending),2
(confirmed),3
(failed),4
(error),5
(worker success),6
(worker failed)transactionStatusMessage
string
A message providing more details about the transaction status. transactionHash
string
The hash of the transaction on the blockchain. curl --location --request GET "https://api.apillon.io/computing/contracts/defa1296-99f4-40de-8470-a2e8497e15ad/transactions" \ +--header "Authorization: Basic :credentials" +
{ + "id": "c761c325-ba82-4861-9a13-866623881b59", + "status": 200, + "data": { + "items": [ + { + "id": 11, + "status": 5, + "walletAddress": "44h63RRA....", + "contractId": 4, + "transactionType": 1, + "transactionStatus": 5, + "transactionStatusMessage": "instantiated", + "transactionHash": "0x4ea088..." + } + ], + "total": 1, + "page": 1, + "limit": 20 + } +} +
Transfer Ownership
Transfer ownership of a computing contract to another address.
POST /computing/contracts/:uuid/transfer-ownershipURL Parameters
Field Type Description Required uuid string
The UUID of the computing contract Yes Body fields
Field Type Description Required accountAddress string
Wallet address of the new owner Yes Possible Errors
Code Description 40012003 Invalid address to transfer to 40012004 Transaction for transfer already exists 50012004 Transfer contract server error 50012009 Contract not in status 3
(Deployed)50012010 Contract transferring or already transferred Response
Success status
curl --location --request POST "https://api.apillon.io/computing/contracts/defa1296-99f4-40de-8470-a2e8497e15ad/transfer-ownership" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +--data-raw "{\"accountAddress\": \"0x456...\"}" +
{ + "status": 200, + "message": "Ownership transferred successfully", + "data": { + "success": true + } +} +
Encrypt Content
Encrypt content with the private key which is securely stored on the Schrodinger contract.
POST /computing/contracts/:uuid/encryptURL Parameters
Field Type Description Required uuid string
The UUID of the computing contract Yes Body fields
Field Type Description Required content string
Content to be encrypted. If the content is an image, the format needs to be base64. Yes Possible errors
Code Description 50012009 Contract not in status 3
(Deployed)50012011 Encrypt content server error Response
The encrypted content, which has been encrypted via the encryption key stored on the schrodinger contract
curl --location --request POST "https://api.apillon.io/computing/contracts/defa1296-99 + +f4-40de-8470-a2e8497e15ad/encrypt" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +--data-raw "{\"content\": \"Hello World\"}" +
{ + "id": "10924c6e-7ad9-449f-bcef-6cbcbdbfcf32", + "status": 201, + "data": { + "encryptedContent": "ebc36a408a68d97150aa6e42e3496a109dc5773c7f5d" + } +} +
Assign CID to NFT
Assign a content identifier (CID) to an NFT within a computing contract. The given token ID represents the NFT which will be used to decrypt the content which is stored as the given CID on IPFS.
POST /computing/contracts/:uuid/assign-cid-to-nftURL Parameters
Field Type Description Required uuid string
The UUID of the computing contract Yes Body fields
Field Type Description Required cid string
CID of the file where the encrypted content is stored Yes nftId number
Token ID of the NFT which will be used for decryption Yes Possible errors
Code Description 50012009 Contract not in status 3
(Deployed)50012012 Assign CID to NFT server error Response
Success status
curl --location --request POST "https://api.apillon.io/computing/contracts/defa1296-99f4-40de-8470-a2e8497e15ad/assign-cid-to-nft" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +--data-raw "{\"cid\": \"QmTzY...\", \"nftId\": 22}" +
{ + "id": "da911d30-bfca-479c-9fb5-3ffa338ddf88", + "status": 201, + "data": { + "success": true + } +} +
Social API | Apillon Wiki + + + + ++ + + diff --git a/fonts/CabinetGrotesk-Variable.ttf b/fonts/CabinetGrotesk-Variable.ttf new file mode 100644 index 00000000..29cffe03 Binary files /dev/null and b/fonts/CabinetGrotesk-Variable.ttf differ diff --git a/fonts/CabinetGrotesk-Variable.woff b/fonts/CabinetGrotesk-Variable.woff new file mode 100644 index 00000000..cfc7ed23 Binary files /dev/null and b/fonts/CabinetGrotesk-Variable.woff differ diff --git a/fonts/CabinetGrotesk-Variable.woff2 b/fonts/CabinetGrotesk-Variable.woff2 new file mode 100644 index 00000000..5462835e Binary files /dev/null and b/fonts/CabinetGrotesk-Variable.woff2 differ diff --git a/fonts/IBMPlexMono-Bold.ttf b/fonts/IBMPlexMono-Bold.ttf new file mode 100644 index 00000000..2ad2fa1d Binary files /dev/null and b/fonts/IBMPlexMono-Bold.ttf differ diff --git a/fonts/IBMPlexMono-Regular.ttf b/fonts/IBMPlexMono-Regular.ttf new file mode 100644 index 00000000..93331e26 Binary files /dev/null and b/fonts/IBMPlexMono-Regular.ttf differ diff --git a/fonts/IBMPlexSans-Bold.ttf b/fonts/IBMPlexSans-Bold.ttf new file mode 100644 index 00000000..e5389d83 Binary files /dev/null and b/fonts/IBMPlexSans-Bold.ttf differ diff --git a/fonts/IBMPlexSans-Regular.ttf b/fonts/IBMPlexSans-Regular.ttf new file mode 100644 index 00000000..b5819647 Binary files /dev/null and b/fonts/IBMPlexSans-Regular.ttf differ diff --git a/fonts/NewSpirit.otf b/fonts/NewSpirit.otf new file mode 100644 index 00000000..786eb600 Binary files /dev/null and b/fonts/NewSpirit.otf differ diff --git a/index.html b/index.html new file mode 100644 index 00000000..089b3a06 --- /dev/null +++ b/index.html @@ -0,0 +1,33 @@ + + + + + + + + +Apillon Wiki Social API
The Social API provides endpoints to list, get and create subsocial entities used in tge Grill widget. Spaces and posts are the main entities provided by it, which may be a bit unfamiliar to a developer who is trying to include the Grill widget into a website. Apillon changes the naming of these entities into Hub and Channel, and takes care of the whole blockchain and IPFS aspect, the result being a simple configuration which can be used to setup a Grill widget on any website.
Note: You can also create channels and hubs in the Apillon dashboard.
Use the Social API to create hubs and channels from your application. For example: you can create a channel (chatroom) for each NFT collection in your marketplace.
In all cURL examples, parameters with a colon as a prefix should be replaced with real values.
Channels
By default, a channel is created inside a hub, therefore a hub can contain multiple channels. The grill widget displays hubs as a list of channels inside it, and a channel is displayed as a chatroom. A channel can be created inside Apillon's default hub or you can create your own hub and create channels within it.
List channels
API to list all channels. Items are paginated and can be filtered and ordered through query parameters as described here .
GET /social/channelsQuery parameters
All query parameters from listing request plus:
Name Description Required hubUuid Parent hub unique identifier false Response fields (channel)
Each item is an instance of channel class, with below properties:
Field Type Description channelUuid string
Channel unique identifier hubUuid string
Unique identifier of the channel's parent Hub channelId number
Channel id on Subsocial chain. This id is used in widget. status number
Channel status ( 1: draft - deploying to chain, 5: active, 100: error
)title string
Channel name body string
Channel body - short description tags string
Comma separated tags createTime DateTime
Item create time updateTime DateTime
Item last update time curl --location --request GET "https://api.apillon.io/social/channels" \ +--header "Authorization: Basic :credentials" +
curl --location --request GET "https://api.apillon.io/social/channels?search=My" \ +--header "Authorization: Basic :credentials" +
{ + "id": "d9ee5982-4292-40ee-b94f-b5c234fecb98", + "status": 200, + "data": { + "items": [ + { + "channelUuid": "6f08bafe-bfd2-4151-bae1-99e515bd6c55", + "hubUuid": "d6355fd3-640d-4803-a4d9-79d875abcb5a", + "channelId": 512, + "status": 1, + "title": "My ApillonAPI channel", + "body": "ApillonAPI channel", + "tags": "web3,fun", + "createTime": "2024-03-05T13:23:20.000Z", + "updateTime": "2024-03-05T13:23:20.000Z", + }, + ... + ], + "total": 4, + "page": 1, + "limit": 20 + } +} +
Get channel
Endpoint to get a single channel. Returns basic channel data.
GET /social/channels/:channelUuidURL parameters
Name Description Required channelUuid Channel unique identifier true Possible errors
Code Description 40419001 Channel does not exists Response fields
Response is an instance of channel class, described above.
curl --location --request GET "https://api.apillon.io/social/channels/:channelUuid" \ +--header "Authorization: Basic :credentials" +
{ + "id": "4a14a0de-6dd5-4863-b031-664aa9b4b13e", + "status": 200, + "data": { + "status": 1, + "createTime": "2024-03-05T13:23:20.000Z", + "updateTime": "2024-03-05T13:23:20.000Z", + "title": "My ApillonAPI channel", + "body": "ApillonAPI channel", + "tags": "web3,fun", + "channelUuid": "6f08bafe-bfd2-4151-bae1-99e515bd6c55", + "channelId": 512 + } +} +
Create channel
API that creates channel and transmits it to blockchain. Transaction can take a while and until confirmed, channel has
status
1.POST /social/channelsBody fields
Name Type Description Required title string
Channel name true body string
Channel content/description true tags string
Comma separated tags false hubUuid string
Hub unique identifier - if not specified the channel is created inside a default hub false Possible errors
Code Description 40419001 Specified hub does not exist 42219002 Body is missing required properties 50019004 Parent hub is in invalid state. It is probably not yet transmitted and confirmed on the chain (status = 1) 50019003 Internal error - Apillon was unable to create channel. Response
Response is an instance of channel class, described above.
curl --location --request POST "https://api.apillon.io/social/channels" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +--data-raw "{ + \"title\": \"Apillon API channel\", + \"body\": \"Lets talk about apillon API\", + \"tags\": \"Apillon,API,WEB3\" +}" +
{ + "id": "68c32a2c-f2b0-4580-916b-2c1b12926228", + "status": 201, + "data": { + "status": 1, + "createTime": "2024-03-06T20:28:12.309Z", + "updateTime": null, + "title": "Apillon API channel", + "body": "Lets talk about apillon API", + "tags": "Apillon,API,WEB3", + "channelUuid": "c1d709b8-16fb-493e-a317-16d8b8ce623d", + "hubUuid": "d6355fd3-640d-4803-a4d9-79d875abcb5a", + "channelId": null + } +} +
Hubs
List hubs
API to list all hubs in project. Items are paginated and can be filtered and ordered through query parameters as described here .
GET /social/hubsResponse fields (hub)
Each item is an instance of hub class, with below properties:
Field Type Description hubUuid string
Hub unique identifier hubId number
Hub id on Subsocial chain. This id is used in widget or in grillapp.net. Example: https://grillapp.net/12927
.status number
Hub status ( 1: draft - deploying to chain, 5: active, 100: error
)name string
Hub name about string
Hub about - short description tags string
Comma separated tags numOfChannels number
Number of channels in hub createTime DateTime
Item create time updateTime DateTime
Item last update time curl --location --request GET "https://api.apillon.io/social/hubs" \ +--header "Authorization: Basic :credentials" +
curl --location --request GET "https://api.apillon.io/storage/buckets?search=My hub" \ +--header "Authorization: Basic :credentials" +
{ + "id": "88b3140f-035e-446d-bda7-1f95e7343619", + "status": 200, + "data": { + "items": [ + { + "hubUuid": "7b59dc14-5ea1-48df-b4c9-a8395690d225", + "hubId": "55545", + "status": 5, + "createTime": "2024-03-05T12:18:20.000Z", + "updateTime": "2024-03-05T13:21:25.000Z", + "name": "Apillon API hub", + "about": "Apillon API hub", + "tags": "web3,fun", + "numOfChannels": 4 + } + ... + ], + "total": 3, + "page": 1, + "limit": 20 + } +} +
Get hub
Endpoint to get hub. Endpoint returns basic hub data.
GET /social/hubs/:hubUuidURL parameters
Name Description Required hubUuid Hub UUID, visible in developer console true Possible errors
Code Description 40419001 Hub does not exists Response fields
Response is an instance of hub class, described above. Only difference is field
numOfChannels
, which is not returned by this endpoint.curl --location --request GET "https://api.apillon.io/social/hubs/:hubUuid" \ +--header "Authorization: Basic :credentials" +
{ + "id": "e4ec496a-121a-4033-9451-5b53ae3d0307", + "status": 200, + "data": { + "status": 5, + "createTime": "2024-03-05T12:18:20.000Z", + "updateTime": "2024-03-05T13:21:25.000Z", + "name": "Apillon API hub", + "about": "Apillon API hub", + "tags": "web3,fun", + "hubUuid": "7b59dc14-5ea1-48df-b4c9-a8395690d225", + "hubId": "55545" + } +} +
Create hub
Endpoint to create a hub and transmit it to blockchain. Transaction can take a while and until confirmed, the hub has
status
1 (Draft).POST /social/hubsBody fields
Name Type Description Required name string
Hub name. true about string
Hub description false tags string
Comma separated tags false Possible errors
Code Description 42219001 Body is missing required properties 50019002 Internal error - Apillon was unable to create hub. Response
Response is an instance of hub class, described above.
curl --location --request POST "https://api.apillon.io/social/hubs" \ +--header "Authorization: Basic :credentials" \ +--header "Content-Type: application/json" \ +--data-raw "{ + \"name\": \"Apillon API hub\", + \"about\": \"Apillon API hub\", + \"tags\": \"Apillon,API,WEB3\" +}" +
{ + "id": "6570cd41-4401-4ba4-92e8-060617f18f65", + "status": 201, + "data": { + "status": 1, + "createTime": "2024-03-06T20:28:12.309Z", + "updateTime": null, + "name": "Apillon API hub", + "about": "Apillon API hub", + "tags": "Apillon,API,WEB3", + "hubUuid": "65527f5b-054d-453a-a9a9-b29cd00b39bb", + "hubId": null + } +} +
Apillon Wiki + + + + ++ + + diff --git a/web3-services/1-good-to-know.html b/web3-services/1-good-to-know.html new file mode 100644 index 00000000..1c5be167 --- /dev/null +++ b/web3-services/1-good-to-know.html @@ -0,0 +1,33 @@ + + + + + + + + +Good to know | Apillon Wiki + + + + ++ + + diff --git a/web3-services/2-web3-storage.html b/web3-services/2-web3-storage.html new file mode 100644 index 00000000..33257a55 --- /dev/null +++ b/web3-services/2-web3-storage.html @@ -0,0 +1,33 @@ + + + + + + + + +Apillon Wiki Good to know
The provision of Apillon Web3 services comes with certain specifics that are important to understand before using the Apillon dashboard.
Before jumping to the API section, please make sure you fully understand what to expect from each service so you can plan your Web3 application architecture and development accordingly.
Apillon integrates multiple Polkadot parachains and offers access to them in a unified way via modules on the developer dashboard and through Apillon APIs.
Concepts
Centralized vs. decentralized
In the current state, Apillon is a Web3 hybrid platform, meaning that some services are still run by centralized solutions. This sort of compromise is quite common in other Web3 solutions, especially where a choice had to be made between fast development and simple UX vs. full decentralization.
That said, Apillon’s back end is designed to evolve towards fully decentralized service and an unstoppable way of operation with each new update, slowly removing the hybrid compromise and becoming a fully Web3 platform.
If you are interested in the details of Apillon’s architecture and back-end, want to open a debate around it or contribute on GitHub, feel free to get in touch in the development channel on Apillon Discord.
Production vs. Beta
Apillon is currently in the Closed Beta stage. Anyone can register an account on Apillon but only assigned users may get access to test the platform’s Beta features.
To join the Apillon Closed Beta program, please follow the following steps.
- If you do not yet have an Apillon account, create one on https://app.apillon.io/register.
- Log in to your account.
- At the bottom of the welcome page, find an ID number assigned to your account.
- Copy the ID number and paste it to the closed-beta channel on Apillon Discord.
- Wait for an email with an invitation to join the Apillon Closed Beta program and access the platform’s features.
Note: Apillon Closed Beta delivers no guarantees related to functionalities or access and is intended for testing purposes only. Beta features also come with several limitations, which will be removed with each platform update as more and more users deliver feedback on Closed Beta and help improve the platform’s services.
Terminology and underlying technology
IPFS
IPFS is a distributed system for storing and accessing files, websites, applications, and data.
When you add a file to IPFS, your file gets split into smaller chunks, is cryptographically hashed, and is given a unique fingerprint called a content identifier (CID). This CID acts as a permanent record of your file as it is at that point in time.
When other nodes search your file, they ask peer nodes which one is storing the content referenced by the file's CID. When viewing or downloading your file, they cache a copy. This way, they become a new provider of your content until their cache is cleared.
Crust Network
Crust Network is a decentralized cloud storage provider that pursues three core Web3 values: decentralization, privacy, and assurance. Crust supports multiple storage-layer protocols, such as IPFS, and delivers instantly accessible on-chain storage functions. Crustʼs technical stack can also support data manipulation and computing.
Crust Network has three main functions: NFT and metaverse metadata storage, personal file storage, and website/dapp hosting.
Storage buckets
Storage buckets are Apillon’s Web3 Storage abstraction that allows developers to utilize IPFS and Crust to store files in an unstoppable, decentralized way.
Learn more about storage buckets.
Web3 Storage | Apillon Wiki + + + + ++ + + diff --git a/web3-services/3-web3-hosting.html b/web3-services/3-web3-hosting.html new file mode 100644 index 00000000..a9f18126 --- /dev/null +++ b/web3-services/3-web3-hosting.html @@ -0,0 +1,33 @@ + + + + + + + + +Apillon Wiki Web3 Storage
Apillon Web3 Storage is a Web3-based storage service that implements AWS S3 (as cache to optimize upload of large files), IPFS, and Crust Network (to pin files on multiple IPFS nodes).
To streamline the development experience, Apillon Web3 Storage service further introduces the concept of storage buckets.
Storage bucket
A storage bucket is a virtual container that holds directories and files in a hierarchical structure. Each directory can contain multiple subdirectories and multiple files, and so on for each subdirectory.
Before using the Apillon Web3 Storage service, a storage bucket should be created on the Apillon Dashboard. Once ready, it enables file storage from both the Apillon dashboard and the API endpoints.
File storage
The process below describes how files are stored with Apillon storage buckets.
- Uploaded files land on a reputable centralized cloud provider to ensure fast file capture.
- Once the files are received, they proceed to the decentralized Apillon IPFS gateway, where Crust Network initiates the pinning and replication process.
- Once the files are accessible on the IPFS network, Crust spreads them through multiple IPFS nodes globally, ensuring file distribution and decentralized accessibility.
File deletion
Each file hosting that passes through Crust’s pinning and replication service is paid upfront for a minimum period of 6 months. Apillon has no control over amending that period, so keep in mind that all files you deploy to Apillon storage buckets will remain accessible for that period of time.
If you decide to delete a file before the 6-month period expires, the file is marked for deletion. This means that Apillon does not extend the storage lease on Crust once the 6-month period expires, which leads to file deletion on all IPFS instances.
However, to make the storage service more dynamic, Apillon artificially lowers the file deletion period to 3 months. Once this period expires, the load of deleted files in your storage bucket is emptied, and the storage capacity is made available for uploading new files.
Note: These limitations are in the nature of the Apillon Closed Beta release, which is intended for testing purposes only. Once the Beta period is up, the limitations of Apillon Web3 services will be adjusted to more realistic production requirements.
Learn more: Web3 Storage FAQ
Web3 Hosting | Apillon Wiki + + + + ++ + + diff --git a/web3-services/4-nfts.html b/web3-services/4-nfts.html new file mode 100644 index 00000000..4f4659ac --- /dev/null +++ b/web3-services/4-nfts.html @@ -0,0 +1,33 @@ + + + + + + + + +Apillon Wiki Web3 Hosting
Apillon Web3 Hosting is a Web3-based storage service that allows you to increase the accessibility of your website or app and make it unstoppable, as it gets hosted on a decentralized network of nodes worldwide.
Decentralized hosting of a website or an app on Apillon is very similar to the usage of storage buckets in decentralized storage. It implements AWS S3 (as cache to optimize upload of large files), IPFS, and Crust Network (to pin files on multiple IPFS nodes).
Website/app hosting
Note: At this point, only hosting of static websites is supported in Apillon Web3 Hosting service, while dynamic websites will be supported in future versions of Apillon.
The process below describes how a static website or app is hosted decentrally with Apillon Web3 Hosting.
- Uploaded files land on a reputable centralized cloud provider to ensure fast file capture.
- Once the files are received, they proceed to the Apillon node. They are accessible through the IPFS gateway until moved from staging to production.
- Once the files move from staging to production, they proceed to the decentralized Apillon IPFS gateway, where the pinning and replication process is started with Crust.
- At this point, a custom domain can be connected to the website/app, which starts the SSL certificate generation process in the Apillon gateway.
- Finally, the domain’s DNS records can be migrated to Apillon, and website or app can be launched using decentralized hosting.
Deployment
To deploy a Web3 website or application, follow the process below:
- Register an account on Apillon.
- Log in to your Apillon dashboard.
- In the menu on the left, under Services, navigate to Hosting, and click "Get started."
- Drag and drop your static website to the Hosting view and wait for the upload to finish.
- Once the upload is complete and the status turns to "successful," you can deploy the website to Staging.
- Click on the Staging tab to monitor the progress.
- Deployment of web files will go through several statuses, ending with "successful."
- Click "Deploy to production" to get files replicated and unstoppable with decentralized hosting.
- Once the deployment to production is finished, click "Add domain" and "Configure domain" to make the domain you own point to the Apillon hosting.
- Once DNS is updated, your unstoppable website will become available on the connected domain.
If you want to redeploy the website or app with new changes, repeat the process above simply by uploading the whole website or app via Apillon Hosting view.
Note: Repeat deployment to Apillon Hosting will continue spending the Hosting storage capacity. However, every 3 months, the capacity will be renewed after older versions are deleted.
Learn more: Web3 Hosting FAQ
File deletion
Each file hosting that passes through Crust’s pinning and replication service is paid upfront for a minimum period of 6 months. Apillon has no control over amending that period, so keep in mind that all files you deploy to Apillon storage buckets will remain accessible for that period of time.
If you decide to delete a file before the 6-month period expires, the file is marked for deletion. This means that Apillon does not extend the storage lease on Crust once the 6-month period expires, which leads to file deletion on all IPFS instances.
However, to make the hosting service more dynamic, Apillon artificially lowers the file deletion period to 3 months. Once this period expires, the load of deleted files in your storage bucket is emptied, and the storage capacity is made available for uploading new files and redeployment of website or app.
Note: In the Apillon Closed Beta stage, single file changes are not supported. Instead, hosting is treated as batch upload, meaning that with every new version of a website or app, its contents are rewritten, which leads to new files getting pinned and replicated on Crust. This limitation will be improved in future dashboard updates to enhance the developer experience. In case you need a larger storage capacity, feel free to get in touch on Apillon Discord, and we will grant you extra space or more storage buckets.
NFTs | Apillon Wiki + + + + ++ + + diff --git a/web3-services/5-web3-authentication.html b/web3-services/5-web3-authentication.html new file mode 100644 index 00000000..813a3364 --- /dev/null +++ b/web3-services/5-web3-authentication.html @@ -0,0 +1,33 @@ + + + + + + + + +Apillon Wiki NFTs
Apillon NFT service supports drag-and-drop compilation, deployment, and minting of non-fungible assets.
The service is currenly supported by the Moonbeam Network and Astar Network for EVM and Astar network for substrate based NFTs.
NFT files
NFT files are at the front and center of an NFT collection.
Apillon NFT Service currently runs on the Moonbeam and Astar parachains, and supports ERC-721 and ERC-6059 standards for NFTs on EVM and PSP-34 standard for substrate.
The ERC-721 standard packs any file format into permanently stored content on the blockchain. Thanks to Moonbeam’s and Astar’s EVM compatibility, it can be deployed quickly and efficiently with Apillon.
NFT metadata
NFT metadata is the backbone of an NFT collection and communicates the essential information of NFT files — the file name and format, date of creation, owner, etc.
It can also include certain attributes that make an NFT one of a kind.
As an essential component of NFT collections, metadata should be managed and stored carefully. To ensure its permanent access free of third-party intervention, Apillon stores it on a decentralized network with Apillon Web3 Storage bucket, Crust Network, and IPFS.
Apillon supports metadata that is structured according to the official OpenSea metadata standard.
NFT deployment
To deploy an NFT collection on either the Moonbeam or Astar Network, follow the process below:
- Log in to your Apillon account.
- In the menu on the left, navigate to NFTs, and click "Start creating NFTs."
- Upload NFT metadata in CSV format to a decentralized network to keep it permanently accessible and prevent losing access to it. When uploaded using the drag-and-drop NFT wizard, the metadata file is automatically stored using the Apillon Web3 Storage bucket. It can also be uploaded manually in the Web3 Storage service and deployed by calling the metadata URI.
- Upload NFT files from local storage, and double-check if the number of files matches the file numbers in the metadata file.
- Preview NFTs, both in table and list view, to verify the parsing of the CSV metadata file. Check for potential mismatches between NFT files and their metadata.
- Choose the name, symbol, and network (Chain) to deploy your collection, either Moonbeam Mainnet, Moonbeam Testnet, or Astar Mainnet.
- Set unlimited or limited supply with the exact number of NFTs you wish to create. Determine your collection's behavior by making NFTs revokable (e.g., with limited-time NFTs) or soulbound to disable tradeability and tie them to the originator's address. Set the % of royalties to collect and the recipient's address. Optionally, you can set the date and details of the collection's public drop and make it publicly available for minting - reserve some for yourself to avoid having to pay for them. Without drop, only the creator can mint NFTs, not the general public. If you sourced metadata online, provide its base URI and extension.
- Preview and go back if needed. This is the last step that allows editing, as moving forward with the deployment inscribes NFTs on the blockchain and makes them immutable.
- Deploy on the blockchain. View the transaction on blockchain explorer or see its contents in the newly created Web3 Storage bucket.
- Display NFTs on a website. Fork or integrate the Apillon website template code in JavaScript, Vue or React on GitHub and add the NFT collection to any webpage. Customize the code, configure the logic, and update the configurable file with the NFT collection's own addresses. Run locally and redeploy to Apillon Web3 Hosting.
- Mint NFTs to trade them as assets on the blockchain. On the Apillon dashboard, click on the NFT collection name, and under Actions in the top right corner, click "Mint". Once minted, NFTs are linked to your wallet.
- List NFTs on marketplaces such as tofuNFT, display them locally on your website or dapp, or share them with intended recipients.
Note: Deploying NFTs on the blockchain makes them permanently immutable. To keep NFT metadata editable, upload it manually to Apillon Web3 Storage with IPNS.
Learn more:
Web3 Authentication | Apillon Wiki + + + + ++ + + diff --git a/web3-services/6-web3-social.html b/web3-services/6-web3-social.html new file mode 100644 index 00000000..9515c25a --- /dev/null +++ b/web3-services/6-web3-social.html @@ -0,0 +1,33 @@ + + + + + + + + +Apillon Wiki Web3 Authentication
Apillon offers a method for users to integrate an alternative authentication approach using a decentralized identifier (DID) stored on the KILT parachain. KILT-powered decentralized identity includes a credential that comprises a user's email address, which can be attested by Apillon or SocialKYC. Any user can create their own decentralized identity, verify their email address through Apillon's OAuth protocol, and then use this decentralized account and credential to verify their identity on third-party platforms.
Authentication workflow
Email Verification: This is the initial step in the process of generating a decentralized identity, which also serves as part of the attestation process, as it attests ownership of the provided email address.
Account Generation: A KILT wallet address derived from a BIP39 mnemonic is created. This mnemonic serves as the master key for accessing the account. Users are prompted to store this mnemonic securely.
Identity Generation: This step typically takes one to five minutes. On the front end, the user signs the operation for DID creation with their Sporran wallet, which is then submitted to the blockchain. The result is a DID document. Once the process is complete, the DID document is queried from the blockchain and returned to the user. All querying occurs on the front end, ensuring that Apillon never gains access to the DID document or the generated mnemonic from the previous step. The user has the option to link their newly generated DID with their account, making it possible to retrieve a DID for an account and vice-versa.
Attestation: Attestation consists of two steps. The first is verifying a user's claim, which is the result of the initial email verification step. The second step involves creating a verifiable credential, signed by both Apillon and the claimer (using the authentication key derived from the generated mnemonic in the second step). This credential's root hash is then submitted to the blockchain. The combination of these steps is referred to as the attestation process, and the credential should be stored securely.
DID and Verifiable Credential Storage: The user is prompted to save the generated files. The generated credential can also be imported into the Sporran wallet for convenience and safekeeping.
Identity Revocation: The user has the option to request the revocation of their DID through Apillon's OAuth website.
The first step mirrors the registration process, where an email with a unique token is sent to the user.
A revocation operation for the DID document is issued to the blockchain, rendering the identity invalid. All associated verifiable credentials also become unusable as a result of this process. The wallet account generated in the second step of the registration process remains valid, and all tokens associated with this account address remain accessible and valid.
Credential Restore: The user can restore their previously generated credentials through Apillon's OAuth website in case they lose access to them. The first step is the same as the registration process and involves sending an email with a unique token. Upon following the verification email link, the user is redirected to a secure page, where the saved credential is returned to them.
Credential Verification: The verification process requires two parameters: a verifiable credential (which can be in JSON format or pasted as plain text in the text area) and the mnemonic passphrase used to sign the credential.
From the provided credential and mnemonic, a presentation is created. The presentation should only contain the user's email address, which is the single property requested by Apillon to complete the verification process. The verification process also checks whether the owner of the credential matches the address that signed the request. The presentation is considered valid if the integrity check mentioned above succeeds.
Alternatively, if a user has imported their previously generated credentials into the Sporran wallet, they can log in directly with Sporran without the need to provide the credentials in text or file format, or the mnemonic passphrase. This allows users to select an existing credential from their wallet and sign the request directly.
Read more: Guide - Log in to Apillon dashboard using KILT decentralized identity
Apillon Open Authentication (OAuth)
The OAuth flow begins when a user interacts with the page, triggering an action that opens the Apillon OAuth pop-up window and initiating the OAuth process. The user can then prove their identity either by providing a credential generated through Apillon and stored by the user or by verifying their credential directly through the Sporran wallet in case they have already imported their credential.
If a user does not yet have a generated decentralized identity (DID) stored on the KILT blockchain, they can also generate one using the OAuth pop-up window.
Once a user completes the verification process, the OAuth pop-up sends an event to the main web application, signaling the completion of the OAuth flow and providing an authentication token. This token can be used by the app to verify the user's identity through Apillon's API.
After this, the web app obtains the user's Apillon account's email address and can use this information to provide access to any services the site offers, ensuring that the user has successfully verified their identity using verifiable credentials attested by Apillon.
Projects, websites, or users interested in offering a decentralized log-in option via Apillon using a verifiable credential can follow the integration guide provided in the integration section.
Web3 Social | Apillon Wiki + + + + ++ + + diff --git a/web3-services/7-web3-compute.html b/web3-services/7-web3-compute.html new file mode 100644 index 00000000..3b8f4ae0 --- /dev/null +++ b/web3-services/7-web3-compute.html @@ -0,0 +1,33 @@ + + + + + + + + +Apillon Wiki Web3 Social
Apillon offers web3 alternatives for social networking between end users. This is powered by subsocial network.
Currently Apillon offers web3 chat alternative called grill chat that allows anonymous chatting which content can be displayed anywhere integrating the subsocial grill chat.
Chat
You can create the chat rooms via Apillon dashboard and integrate grill chat widget into you page via
html
andjavascript
. You can also manage you chat rooms via API and SDK for simple on the fly creation.You can create a single chat room or a hub containing multiple chat rooms.
Hub A hub is a container of multiple chat rooms. When integrating the grill chat widget you can either show a single chat or a whole hub containing multiple chat rooms that the end user selects to chat in.
Channel Channel is the actual chat room the users can talk in. s
Web3 Compute power | Apillon Wiki + + + + ++ + +Apillon Wiki Web3 Compute power
In the world of web3 we have smart contracts that power some core backend logic that should be on chain, we have ipfs hosting to host our webpages in an unstoppable manner. But running actual backend code in an decentralized manner is a missing piece. At Apillon we are tackling this problem and researching different options of how to provide fully distributed and unstoppable backend. Currently this results in a specific service for fully decentralized NFT gated file access powered by Phala.
NFT gated file access
Phala PHAT contracts are utilized to provide file encryption capabilities where there is no one that can access the encryption key. But a user with a specific NFT can utilized it. This results in a NFT gated fully encrypted file storage that is unstoppable in every way.
Through Apillon you can deploy and configure the PHAT contract and use preexisting frontend templates to fully use the functionalities.
For using the NFT gated file access there are a few prerequisites:
- You need to create a NFT collection. When deploying a PHAT contract you need to specify a NFT collection to which it is tied. And for each NFT you can either set the encrypted content that only the NFT owner will be able to access or make the NFT owner the one that can upload the file to storage.
- You need a IPFS storage bucket. Any content uploaded via PHAT contract will need to be stored somewhere. For this you need to create an IPFS bucket (to make sure the content is distributed. Only encrypted content is ever stored).
Which this prerequisites done you can then set this information and deploy you PHAT contract. Deployment and execution costs are handled by Apillon.
You can create a PHAT contract either though the dashboard or via API / SDK.