Skip to content

Commit

Permalink
Add deploy system for public folder
Browse files Browse the repository at this point in the history
Merge pull request #5 from ItsAnunesS/main
ItsAnunesS authored Nov 15, 2022
2 parents 7651cda + 117241d commit b44d474
Showing 14 changed files with 882 additions and 201 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-permawidget-vue",
"version": "1.0.2",
"version": "1.1.0",
"description": "🛠️ The Create Permapages widget boilerplate",
"bin": {
"create-permawidget-vue": "./tasks/cpa.js"
1 change: 1 addition & 0 deletions template/arweave-storage.json

Large diffs are not rendered by default.

275 changes: 254 additions & 21 deletions template/deploy.mjs
Original file line number Diff line number Diff line change
@@ -1,33 +1,81 @@
import Arweave from "arweave";
import path from "path";
import assert from "assert";
import fs from "fs";
import mime from "mime-types";

const walletFile = process.argv[2];
assert(walletFile, "Wallet required!");

console.log("Wallet file:", walletFile);
const rootPath = process.cwd();

const dist = path.join(rootPath, "dist");
if (!fs.existsSync(dist)) {
log(
"Error: Build not found!\n\t\x1b[0mPlease run `npm run build` first.",
"error"
);
process.exit();
}
const widgetPath = path.join(dist, "widget.js");
const pkg = JSON.parse(fs.readFileSync("./package.json"));
const jwk = JSON.parse(fs.readFileSync(walletFile).toString());
const arweave = Arweave.init({
host: "arweave.net",
port: 443,
protocol: "https",
timeout: 20000,
logging: false,
});

assert(walletFile, "Wallet required!");
main();

try {
const jwk = JSON.parse(fs.readFileSync(walletFile).toString());
function main() {
log("Starting Deploy...");
deployPublic()
.then(() => {
log("Deploying Widget...");
return deployWidget();
})
.then((arweave) => {
log(
`Widget Deployed!!\n you can check here: https://arweave.net/${arweave.id}`,
"success"
);
});
}

async function deployPublic() {
const files = readFilesSync("public");
log("Deploying Public Files...");

const arweave = Arweave.init({
host: "arweave.net",
port: 443,
protocol: "https",
timeout: 20000,
logging: false,
files.forEach((file) => {
log(`Deploying ${file.relativePath}..`);
deployFileSync(file).then((file) => {
editWidget(file);
});
});
}

const data = fs.readFileSync("./dist/widget.js");
function editWidget(file) {
if (file) {
const widget = fs.readFileSync(widgetPath).toString();
const widgetNew = widget.replace(file.find, file.url);
fs.writeFileSync(widgetPath, widgetNew);
return widgetPath;
}

throw new Error(
`Error Edit Widget: File not found!\n${JSON.stringify(file)}`
);
}

async function deployWidget() {
const tags = [
{ name: "Content-Type", value: "application/javascript" },
{ name: "App-Name", value: "Permapage-Widget" },
{ name: "App-Version", value: "0.0.1" },
{ name: "Widget-Id", value: "latest-arprofile" },
{ name: "Widget-Id", value: pkg.name },
{ name: "Widget-Name", value: pkg.name },
{ name: "Widget-Version", value: pkg.version },
{ name: "Widget-Desc", value: pkg.description },
@@ -37,15 +85,200 @@ try {
},
];

const tx = await arweave.createTransaction({ data }, jwk);
tags.forEach((tag) => tx.addTag(tag.name, tag.value));
await arweave.transactions.sign(tx, jwk);
const result = await arweave.transactions.post(tx);
const tx = await arweaveDeployFile(widgetPath, tags);

if (tx) {
return tx;
}

throw new Error("Error Deploy Widget!");
}

async function deployFileSync(file) {
if (file) {
const filepath = file.filepath;
const fileMime = file.fileMime;
const isDeployed = isFileDeployed(file);

var result = false;
if (!isDeployed) {
await arweaveDeployFile(file, {
name: "Content-Type",
value: fileMime,
});
} else {
log(`File ${filepath} get from storage!`, "success");
}

result = getStorageItem(file.relativePath);

if (!result) {
log(`File ${filepath} not deployed!`, "error");
}

return result;
}

throw new Error(
`Error Deploy File: File not found!\n${JSON.stringify(file)}`
);
}

function isFileDeployed(file) {
if (file) {
const relativePath = file.relativePath;
const stat = file.stat;
const storage = path.join(rootPath, "arweave-storage.json");
if (fs.existsSync(storage)) {
const storageData = JSON.parse(fs.readFileSync(storage));
if (storageData[relativePath]) {
const storageFile = storageData[relativePath];
if (
storageFile.stat.mtimeMs >= stat.mtimeMs &&
storageFile.stat.size === stat.size
) {
return getStorageItem(relativePath);
}
}
} else {
return false;
}
}

throw new Error(
`Error Check File Deployed: File not found!\n${JSON.stringify(file)}`
);
}

function getStorageItem(relativePath) {
if (relativePath) {
const storage = path.join(rootPath, "arweave-storage.json");
if (fs.existsSync(storage)) {
return JSON.parse(fs.readFileSync(storage))[relativePath];
}
return false;
}

throw new Error(
`Error Get Storage Item: File not found!\n${JSON.stringify(relativePath)}`
);
}

function addStorageItem(file, arweave) {
if (file) {
const storage = path.join(rootPath, "arweave-storage.json");
var storageData = {};
if (fs.existsSync(storage)) {
storageData = JSON.parse(fs.readFileSync(storage).toString());
}

const find = file.relativePath.toString().replace("public", "");
storageData[file.relativePath] = {
name: file.name,
find: find,
ext: file.ext,
mime: file.fileMime,
path: file.filepath,
stat: file.stat,
url: `https://arweave.net/${arweave.id}`,
arweave: arweave,
};
fs.writeFileSync(storage, JSON.stringify(storageData));
return storageData;
}

throw new Error(
`Error Add Storage Item: File not found!\n${JSON.stringify(file)}`
);
}

function readFilesSync(dir) {
var files = [];

fs.readdirSync(dir).forEach((filename) => {
const name = path.parse(filename).name;
const ext = path.parse(filename).ext;
const filepath = path.resolve(dir, filename);
const relativePath = path.relative(rootPath, filepath);
const fileMime = mime.lookup(filepath);
const stat = fs.statSync(filepath);
const isFile = stat.isFile();

if (isFile) {
files.push({ filepath, relativePath, fileMime, name, ext, stat });
} else {
files = files.concat(readFilesSync(filepath));
}
});

files.sort((a, b) => {
return a.name.localeCompare(b.name, undefined, {
numeric: true,
sensitivity: "base",
});
});

return files;
}

function arweaveDeployFile(file, tags) {
if (file) {
const filepath = "object" === typeof file ? file.filepath : file;
const data = fs.readFileSync(filepath);

var transaction;
return arweave
.createTransaction({ data }, jwk)
.then((tx) => {
if (0 < tags.length) {
tags.forEach((tag) => tx.addTag(tag.name, tag.value));
}
transaction = tx;
return arweave.transactions.sign(tx, jwk);
})
.then(() => {
return arweave.transactions.post(transaction);
})
.then(() => {
log(
`${filepath} deployed!
\x1b[0mTransactionId: ${transaction.id}
https://arweave.net/${transaction.id}`
);
if (file && "object" === typeof file) {
addStorageItem(file, transaction);
}
return transaction;
})
.catch((e) => {
throw e;
});
}

throw new Error(
`Error Arweave Deploy: File not found!\n${JSON.stringify(file)}`
);
}

function log(message, type = "info") {
type = type.toLowerCase();
if (
type != "info" &&
type != "error" &&
type != "success" &&
type != "warn"
) {
type = "info";
}

console.log("Deployed!");
const color = {
info: "\x1b[36m",
error: "\x1b[31m",
success: "\x1b[32m",
warn: "\x1b[33m",
};

console.log("result: ", result);
console.log("TransactionId: ", tx.id);
} catch (e) {
console.log("ERROR: ", e);
console.log(
`${color[type]}\x1b[1m[Permawidget VueJS]\x1b[0m${color[type]} ${message}\x1b[0m`
);
}
487 changes: 467 additions & 20 deletions template/package-lock.json

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions template/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "create-permawidget-vue",
"version": "1.0.0",
"description": "🛠️ The Permapages widget boilerplate",
"version": "1.1.3",
"description": "🛠️ The Permapages widget boilerplate sample for Vue.js",
"author": {
"name" : "André Nunes",
"email" : "hello@anuness.dev",
"url" : "https://anuness.dev/"
"name": "André Nunes",
"email": "hello@anuness.dev",
"url": "https://anuness.dev/"
},
"repository": {
"type": "git",
@@ -24,7 +24,9 @@
"deploy": "node deploy.mjs"
},
"dependencies": {
"arweave": "^1.11.6",
"daisyui": "^2.31.0",
"mime-types": "^2.1.35",
"pinia": "^2.0.21",
"vite-plugin-css-injected-by-js": "^2.1.1",
"vue": "^3.2.38",
Binary file removed template/public/favicon.ico
Binary file not shown.
Binary file added template/public/img/metaweave.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
111 changes: 2 additions & 109 deletions template/src/components/WidgetComponent.vue
Original file line number Diff line number Diff line change
@@ -1,101 +1,8 @@
<script setup lang="ts">
import { useClicksStore } from "@/stores/clicks";
import Twitter from "@/components/icons/TwitterIcon.vue";
import GitHub from "@/components/icons/GitHubIcon.vue";
import { ref } from "vue";
const clicks = useClicksStore();
const steps = ref(0);
const { getClicks, resetClicks, increaseClicks, decreaseClicks, getUser, getName, getAge, resetUser, setUser, setName, setAge, } = clicks;
</script>
<script setup lang="ts"></script>

<template>
<div class="widget shadow-xl text-white">
<ul class="steps">
<li :class="'step ' + ((steps === 0 || steps === 1 || steps === 2) ? 'step-primary' : '')">Fake Register</li>
<li :class="'step ' + ((steps === 1 || steps === 2) ? 'step-primary' : '')">Click</li>
<li :class="'step ' + ((steps === 2) ? 'step-primary' : '')">finish</li>
</ul>
<div v-if="steps == 0">
<div class="my-6">
<h1 class="text-xl">Hello {{ getName() }}</h1>
<h2 class="text-lg">your age is: {{ getAge() }}</h2>
</div>
<div class="form-control w-full max-w-xs my-6">
<label class="label">
<span class="label-text text-white">What is your name?</span>
</label>
<input type="text" placeholder="Type here" @input="(e: Event) => { setName(e.target?.value) }"
class="input input-bordered w-full max-w-xs" />
</div>
<div class="form-control w-full max-w-xs my-6">
<label class="label">
<span class="label-text text-white">What is your Age?</span>
</label>
<input type="text" placeholder="Type here" @input="(e: Event) => { setAge(e.target?.value) }"
class="input input-bordered w-full max-w-xs" />
</div>
<div>
<button
class="inline-block px-6 py-2.5 bg-blue-600 text-white font-medium text-xs leading-tight uppercase rounded shadow-md hover:bg-blue-700 hover:shadow-lg focus:bg-blue-700 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-blue-800 active:shadow-lg transition duration-150 ease-in-out"
@click="steps++">Next</button>
</div>
</div>
<div v-if="steps == 1">
<div class="mt-10">
<h1 class="tetx-xl">Total Clicks: {{ getClicks() }}</h1>
<button
class="inline-block px-6 py-2.5 bg-blue-600 text-white font-medium text-xs leading-tight uppercase rounded shadow-md hover:bg-blue-700 hover:shadow-lg focus:bg-blue-700 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-blue-800 active:shadow-lg transition duration-150 ease-in-out mx-3 mt-4"
@click="increaseClicks()">Click</button>
<div class="mt-10">
<button
class="inline-block px-6 py-2.5 bg-blue-600 text-white font-medium text-xs leading-tight uppercase rounded shadow-md hover:bg-blue-700 hover:shadow-lg focus:bg-blue-700 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-blue-800 active:shadow-lg transition duration-150 ease-in-out mx-3"
@click="steps--">Previous</button>
<button
class="inline-block px-6 py-2.5 bg-blue-600 text-white font-medium text-xs leading-tight uppercase rounded shadow-md hover:bg-blue-700 hover:shadow-lg focus:bg-blue-700 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-blue-800 active:shadow-lg transition duration-150 ease-in-out mx-3"
@click="steps++">next</button>
</div>
</div>
</div>
<div v-if="steps == 2">
<div class="my-10">
<h1 class="text-xl">Thank you for your time {{ getName() }}</h1>
<h2 class="text-lg">your age is: {{ getAge() }}</h2>
<h2 class="text-lg">your total clicks is: {{ getClicks() }}</h2>
<p class="p-4">Please reload the page</p>
</div>
</div>
<div class="my-5">
<h2 class="my-3">Reset</h2>
<button
class="inline-block px-6 py-2.5 bg-blue-600 text-white font-medium text-xs leading-tight uppercase rounded shadow-md hover:bg-blue-700 hover:shadow-lg focus:bg-blue-700 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-blue-800 active:shadow-lg transition duration-150 ease-in-out mx-4"
@click="resetUser">User Details</button>

<button
class="inline-block px-6 py-2.5 bg-blue-600 text-white font-medium text-xs leading-tight uppercase rounded shadow-md hover:bg-blue-700 hover:shadow-lg focus:bg-blue-700 focus:shadow-lg focus:outline-none focus:ring-0 active:bg-blue-800 active:shadow-lg transition duration-150 ease-in-out mx-4"
@click="resetClicks">Clicks</button>
</div>
<div class="my-5">
<h2 class="my-3">Social Medias</h2>
<div class="flex justify-around">
<a href="https://github.com/itsanuness">
<GitHub />
</a>
<a href="https://twitter.com/itsanuness">
<Twitter />
</a>
</div>
</div>
<div class="mt-8">
<h1 class="text-lg">This template is open source!!</h1>
<h2 class="my-3">fork it!!</h2>
<div class="flex justify-center">
<a href="https://github.com/ItsAnunesS/vuejs-widget-template">
<GitHub />
</a>
</div>
</div>
<img src="/img/metaweave.png" alt="Metaweave Logo">
</div>
</template>

@@ -121,18 +28,4 @@ const { getClicks, resetClicks, increaseClicks, decreaseClicks, getUser, getName
rgba(188, 0, 255, 1) 19%,
rgba(248, 23, 181, 1) 100%);
}
.loading {
display: inline-block;
width: 2rem;
height: 2rem;
border: 0.25rem solid #fff;
border-radius: 50%;
border-top-color: #1a1a1a;
animation: spin 1s ease-in-out infinite;
}
a {
text-shadow: 0px 0px 20px #000000;
}
</style>
29 changes: 0 additions & 29 deletions template/src/components/icons/GitHubIcon.vue

This file was deleted.

10 changes: 0 additions & 10 deletions template/src/components/icons/TwitterIcon.vue

This file was deleted.

2 changes: 1 addition & 1 deletion template/src/main.ts
Original file line number Diff line number Diff line change
@@ -11,4 +11,4 @@ const app = createApp(App);
app.use(createPinia());
app.use(router);

app.mount("#widget-template");
app.mount("#create-permawidget-vue");
3 changes: 0 additions & 3 deletions template/tailwind.config.js
Original file line number Diff line number Diff line change
@@ -4,9 +4,6 @@ module.exports = {
theme: {
extend: {
colors: {
mtw_purple: "#f817b5",
mtw_white_alt: "#f9f9f9",
mtw_white_alt2: "#f3f4ff",
},
},
},
147 changes: 147 additions & 0 deletions template/test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import path from "path";
import fs from "fs";
import mime from "mime-types";
import Arweave from "arweave";

//joining path of directory
const currentPath = process.cwd();
const directoryPath = path.join(currentPath, "public");
const walletFile = process.argv[2];
const jwk = JSON.parse(fs.readFileSync(walletFile).toString());
const arweave = Arweave.init({
host: "arweave.net",
port: 443,
protocol: "https",
timeout: 20000,
logging: false,
});
const files = readFilesSync(directoryPath);
files.forEach(async (file) => {
const isDeployed = isFileDeployed(file);

if (!isDeployed) {
const res = await arweaveDeployFile(file.filepath, [
{ name: "Content-Type", value: file.fileMime },
]);
const storage = path.join(currentPath, "arweave-storage.json");
var storageData = {};
if (fs.existsSync(storage)) {
storageData = JSON.parse(fs.readFileSync(storage).toString());
}
storageData[file.relativePath] = {
name: file.name,
find: file.relativePath.replace("public", ""),
ext: file.ext,
mime: file.fileMime,
path: file.filepath,
stat: file.stat,
arweave: res,
};
fs.writeFileSync(storage, JSON.stringify(storageData));
} else {
console.log("File already deployed");
console.log(isDeployed);
}
});

function isFileDeployed(file) {
if (file) {
const relativePath = file.relativePath;
const stat = file.stat;
const storage = path.join(currentPath, "arweave-storage.json");
if (fs.existsSync(storage)) {
const storageData = JSON.parse(fs.readFileSync(storage).toString());
if (storageData[relativePath]) {
const storageFile = storageData[relativePath];
if (
storageFile.stat.mtimeMs >= stat.mtimeMs &&
storageFile.stat.size === stat.size
) {
return storageFile;
}
}
}
}
return false;
}

function readFilesSync(dir) {
var files = [];

fs.readdirSync(dir).forEach((filename) => {
const name = path.parse(filename).name;
const ext = path.parse(filename).ext;
const filepath = path.resolve(dir, filename);
const relativePath = path.relative(currentPath, filepath);
const fileMime = mime.lookup(filepath);
const stat = fs.statSync(filepath);
const isFile = stat.isFile();

if (isFile) {
files.push({ filepath, relativePath, fileMime, name, ext, stat });
} else {
files = files.concat(readFilesSync(filepath));
}
});

files.sort((a, b) => {
return a.name.localeCompare(b.name, undefined, {
numeric: true,
sensitivity: "base",
});
});

return files;
}

async function arweaveDeployFile(filepath, tags) {
const data = fs.readFileSync(filepath);

var transaction;
return arweave
.createTransaction({ data }, jwk)
.then((tx) => {
if (0 > tags.length) {
tags.forEach((tag) => tx.addTag(tag.name, tag.value));
}
transaction = tx;
return arweave.transactions.sign(tx, jwk);
})
.then(() => {
return arweave.transactions.post(transaction);
})
.then((res) => {
log(
`${filepath} deployed! \n\x1b[0mTransactionId: ${
transaction.id
}\n${JSON.stringify(res)}`
);
return transaction;
})
.catch((e) => {
throw e;
});
}

function log(message, type = "info") {
type = type.toLowerCase();
if (
type != "info" &&
type != "error" &&
type != "success" &&
type != "warn"
) {
type = "info";
}

const color = {
info: "\x1b[36m",
error: "\x1b[31m",
success: "\x1b[32m",
warn: "\x1b[33m",
};

console.log(
`${color[type]}\x1b[1m[Permawidget VueJS]\x1b[0m${color[type]} ${message}`
);
}

0 comments on commit b44d474

Please sign in to comment.