Skip to content

Commit

Permalink
Adds studio app
Browse files Browse the repository at this point in the history
  • Loading branch information
peterpolman committed Sep 17, 2024
1 parent 51cd9f6 commit ba4b4eb
Show file tree
Hide file tree
Showing 79 changed files with 4,442 additions and 144 deletions.
5 changes: 3 additions & 2 deletions apps/api/scripts/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ import db from '@thxnetwork/api/util/database';
// import main from './src/preview';
// import main from './src/metamask';
// import main from './src/lottery';
import main from './src/web3';
// import main from './src/web3';
// import main from './src/safe';
import main from './src/qr';

db.connect(process.env.MONGODB_URI_PROD);
db.connect(process.env.MONGODB_URI);

main()
.then(() => process.exit(0))
Expand Down
38 changes: 38 additions & 0 deletions apps/api/scripts/src/qr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { QRCodeEntry, RewardNFT } from '@thxnetwork/api/models';
import PoolService from '@thxnetwork/api/services/PoolService';
import { PromiseParser } from '@thxnetwork/api/util';

export default async function main() {
const chunkSize = 1000;

let skip = 0;
let entries = [];
console.log('start while', entries.length);
while (true) {
entries = await QRCodeEntry.find().skip(skip).limit(chunkSize).exec();
if (!entries.length) break;

const operations = await PromiseParser.parse(
entries.map(async (entry) => {
const reward = await RewardNFT.findById(entry.rewardId);
const pool = await PoolService.getById(reward.poolId);
const owner = await PoolService.findOwner(pool);
return {
updateOne: {
filter: { _id: entry._id },
update: {
$set: {
accountId: owner._id,
},
},
},
};
}),
);

console.log(operations.length);
await QRCodeEntry.bulkWrite(operations);

skip += chunkSize;
}
}
1 change: 1 addition & 0 deletions apps/api/src/app/config/secrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ export const WALLET_URL = process.env.WALLET_URL || '';
export const DASHBOARD_URL = process.env.DASHBOARD_URL || '';
export const WIDGET_URL = process.env.WIDGET_URL || '';
export const PUBLIC_URL = process.env.PUBLIC_URL || '';
export const STUDIO_URL = process.env.STUDIO_URL || '';

export const HARDHAT_RPC = process.env.HARDHAT_RPC || '';
export const POLYGON_RPC = process.env.POLYGON_RPC || 'https://rpc.ankr.com/polygon';
Expand Down
2 changes: 0 additions & 2 deletions apps/api/src/app/controllers/erc721/post.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ const validation = [
];

const controller = async (req: Request, res: Response) => {
// #swagger.tags = ['ERC721']

const logoImgUrl = req.file && (await ImageService.upload(req.file));
const forceSync = req.query.forceSync !== undefined ? req.query.forceSync === 'true' : false;
const account = await AccountProxy.findById(req.auth.sub);
Expand Down
20 changes: 5 additions & 15 deletions apps/api/src/app/controllers/qr-codes/list.controller.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
import { QRCodeEntry, RewardNFT } from '@thxnetwork/api/models';
import { ForbiddenError, NotFoundError } from '@thxnetwork/api/util/errors';
import { QRCodeEntry } from '@thxnetwork/api/models';
import { Request, Response } from 'express';
import { query } from 'express-validator';
import AccountProxy from '@thxnetwork/api/proxies/AccountProxy';
import PoolService from '@thxnetwork/api/services/PoolService';

const validation = [query('rewardId').isMongoId(), query('page').isInt(), query('limit').isInt()];
const validation = [query('limit').optional().isInt({ gt: 0 }), query('page').optional().isInt({ gt: 0 })];

const controller = async (req: Request, res: Response) => {
const page = Number(req.query.page);
const limit = Number(req.query.limit);
const rewardId = req.query.rewardId;

const reward = await RewardNFT.findById(rewardId);
if (!reward) throw new NotFoundError('Reward not found');

const isAllowed = await PoolService.isSubjectAllowed(req.auth.sub, reward.poolId);
if (!isAllowed) throw new ForbiddenError('Reward not accessible.');

const total = await QRCodeEntry.countDocuments({ rewardId });
const entries = await QRCodeEntry.find({ rewardId })
const total = await QRCodeEntry.countDocuments({ accountId: req.auth.sub });
const entries = await QRCodeEntry.find({ accountId: req.auth.sub })
.limit(limit)
.skip((page - 1) * limit);
const subs = entries.map(({ sub }) => sub);
Expand All @@ -29,7 +19,7 @@ const controller = async (req: Request, res: Response) => {
return Object.assign(entry.toJSON(), { account });
});
const meta = {
participantCount: await QRCodeEntry.countDocuments({ rewardId, sub: { $exists: true } }),
participantCount: await QRCodeEntry.countDocuments({ sub: { $exists: true } }),
};

res.json({
Expand Down
9 changes: 1 addition & 8 deletions apps/api/src/app/controllers/qr-codes/post.controller.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { RewardNFT } from '@thxnetwork/api/models';
import { NotFoundError } from '@thxnetwork/api/util/errors';
import { Request, Response } from 'express';
import { body } from 'express-validator';
import QRCodeService from '@thxnetwork/api/services/ClaimService';
Expand All @@ -11,14 +9,9 @@ const validation = [
];

const controller = async (req: Request, res: Response) => {
const rewardId = req.body.rewardId;
const redirectURL = req.body.redirectURL;
const claimAmount = Number(req.body.claimAmount);

const reward = await RewardNFT.findById(rewardId);
if (!reward) throw new NotFoundError('Reward not found');

const entries = await QRCodeService.create({ rewardId, redirectURL }, claimAmount);
const entries = await QRCodeService.create({ accountId: req.auth.sub, redirectURL }, claimAmount);

res.status(201).json(entries);
};
Expand Down
3 changes: 2 additions & 1 deletion apps/api/src/app/middlewares/corsHandler.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import cors from 'cors';
import { AUTH_URL, API_URL, DASHBOARD_URL, WIDGET_URL, PUBLIC_URL } from '@thxnetwork/api/config/secrets';
import { AUTH_URL, API_URL, DASHBOARD_URL, WIDGET_URL, PUBLIC_URL, STUDIO_URL } from '@thxnetwork/api/config/secrets';

export const corsHandler = cors(async (req: any, callback: any) => {
const origin = req.header('Origin');
const allowedOrigins = [
AUTH_URL,
API_URL,
DASHBOARD_URL,
STUDIO_URL,
WIDGET_URL,
PUBLIC_URL,
'https://thx.network',
Expand Down
1 change: 1 addition & 0 deletions apps/api/src/app/models/QRCodeEntry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const QRCodeEntry = mongoose.model<QRCodeEntryDocument>(
'QRCodeEntry',
new mongoose.Schema(
{
accountId: String,
sub: String,
uuid: String,
redirectURL: String,
Expand Down
2 changes: 1 addition & 1 deletion apps/api/src/app/services/ERC721Service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { ADDRESS_ZERO } from '../config/secrets';

const contractName = 'THXERC721';

async function deploy(data: TERC721, forceSync = true): Promise<ERC721Document> {
async function deploy(data: Partial<TERC721>, forceSync = true): Promise<ERC721Document> {
const { web3, defaultAccount } = NetworkService.getProvider(data.chainId);
const { abi, bytecode } = getArtifact(contractName);
const contract = new web3.eth.Contract(abi);
Expand Down
2 changes: 0 additions & 2 deletions apps/app/src/types/interfaces/account.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,5 +82,3 @@ type TWidgetConfig = {
backgroundUrl: string;
ref?: string;
};

type TOAuthScope = OAuthGoogleScope | OAuthTwitterScope | OAuthDiscordScope | OAuthTwitchScope | OAuthGithubScope;
1 change: 1 addition & 0 deletions apps/studio/.build.env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NODE_OPTIONS="--max-old-space-size=8192 --openssl-legacy-provider"
4 changes: 4 additions & 0 deletions apps/studio/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
VITE_API_URL=""
VITE_AUTH_URL=""
VITE_CLIENT_ID=""
VITE_CLIENT_SECRET=""
28 changes: 28 additions & 0 deletions apps/studio/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"extends": [
"../../.eslintrc.json",
"plugin:vue/essential",
"plugin:vue/vue3-recommended",
"@vue/eslint-config-prettier",
"@vue/eslint-config-typescript"
],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.ts", "*.vue"],
"rules": {
// no-undef is not working with TS projects, ref: https://typescript-eslint.io/troubleshooting/#i-get-errors-from-the-no-undef-rule-about-global-variables-not-being-defined-even-though-there-are-no-typescript-errors
"no-undef": "off",
"no-debugger": "off",
"@typescript-eslint/no-explicit-any": "off",
"@nx/enforce-module-boundaries": "off",
"vue/require-explicit-emits": "off",
"vue/require-default-prop": "off",
"vue/require-v-for-key": "off",
"vue/valid-v-for": "off",
"vue/no-multiple-template-root": "off",
"vue/return-in-computed-property": "off"
}
}
]
}
Empty file added apps/studio/.gitkeep
Empty file.
75 changes: 75 additions & 0 deletions apps/studio/components.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}

declare module 'vue' {
export interface GlobalComponents {
Alert: typeof import('./src/components/ui/alert/Alert.vue')['default']
AlertDescription: typeof import('./src/components/ui/alert/AlertDescription.vue')['default']
AlertTitle: typeof import('./src/components/ui/alert/AlertTitle.vue')['default']
BAlert: typeof import('bootstrap-vue-next')['BAlert']
BaseCardCollectionMetadata: typeof import('./src/components/BaseCardCollectionMetadata.vue')['default']
BaseCardTableHeader: typeof import('./src/components/BaseCardTableHeader.vue')['default']
BaseFormCollectionMetadata: typeof import('./src/components/BaseFormCollectionMetadata.vue')['default']
BaseFormCollectionMetadataQRCodes: typeof import('./src/components/BaseFormCollectionMetadataQRCodes.vue')['default']
BaseFormGroup: typeof import('./src/components/BaseFormGroup.vue')['default']
BaseFormInputFile: typeof import('./src/components/BaseFormInputFile.vue')['default']
BaseHrOr: typeof import('./src/components/BaseHrOr.vue')['default']
BaseHrOrSeparator: typeof import('./src/components/BaseHrOrSeparator.vue')['default']
BaseIcon: typeof import('./src/components/BaseIcon.vue')['default']
BaseModal: typeof import('./src/components/BaseModal.vue')['default']
BaseModalNFTCreate: typeof import('./src/components/BaseModalNFTCreate.vue')['default']
BaseModalQRCode: typeof import('./src/components/BaseModalQRCode.vue')['default']
BaseOrSeparator: typeof import('./src/components/BaseOrSeparator.vue')['default']
BAvatar: typeof import('bootstrap-vue-next')['BAvatar']
BBrand: typeof import('bootstrap-vue-next')['BBrand']
BButton: typeof import('bootstrap-vue-next')['BButton']
BCard: typeof import('bootstrap-vue-next')['BCard']
BCol: typeof import('bootstrap-vue-next')['BCol']
BCollapse: typeof import('bootstrap-vue-next')['BCollapse']
BContainer: typeof import('bootstrap-vue-next')['BContainer']
BDropdown: typeof import('bootstrap-vue-next')['BDropdown']
BDropdownItem: typeof import('bootstrap-vue-next')['BDropdownItem']
BForm: typeof import('bootstrap-vue-next')['BForm']
BFormFile: typeof import('bootstrap-vue-next')['BFormFile']
BFormGroup: typeof import('bootstrap-vue-next')['BFormGroup']
BFormInput: typeof import('bootstrap-vue-next')['BFormInput']
BFormSelect: typeof import('bootstrap-vue-next')['BFormSelect']
BFormTextare: typeof import('bootstrap-vue-next')['BFormTextare']
BFormTextarea: typeof import('bootstrap-vue-next')['BFormTextarea']
BIcon: typeof import('bootstrap-vue-next')['BIcon']
BImg: typeof import('bootstrap-vue-next')['BImg']
BInputGroup: typeof import('bootstrap-vue-next')['BInputGroup']
BJumbotron: typeof import('bootstrap-vue-next')['BJumbotron']
BLink: typeof import('bootstrap-vue-next')['BLink']
BListGroup: typeof import('bootstrap-vue-next')['BListGroup']
BListGroupItem: typeof import('bootstrap-vue-next')['BListGroupItem']
BListGroupp: typeof import('bootstrap-vue-next')['BListGroupp']
BModal: typeof import('bootstrap-vue-next')['BModal']
BModel: typeof import('bootstrap-vue-next')['BModel']
BNavbar: typeof import('bootstrap-vue-next')['BNavbar']
BNavbarBrand: typeof import('bootstrap-vue-next')['BNavbarBrand']
BNavbarNav: typeof import('bootstrap-vue-next')['BNavbarNav']
BNavbarToggle: typeof import('bootstrap-vue-next')['BNavbarToggle']
BNavItem: typeof import('bootstrap-vue-next')['BNavItem']
BNavItemDropdown: typeof import('bootstrap-vue-next')['BNavItemDropdown']
BPagination: typeof import('bootstrap-vue-next')['BPagination']
BPlaceholder: typeof import('bootstrap-vue-next')['BPlaceholder']
BPlaceholderButton: typeof import('bootstrap-vue-next')['BPlaceholderButton']
BPlaceholderCard: typeof import('bootstrap-vue-next')['BPlaceholderCard']
BRow: typeof import('bootstrap-vue-next')['BRow']
BSpinner: typeof import('bootstrap-vue-next')['BSpinner']
BTable: typeof import('bootstrap-vue-next')['BTable']
Button: typeof import('./src/components/ui/button/Button.vue')['default']
copy: typeof import('./src/components/BaseFormGroup copy.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
export interface ComponentCustomProperties {
vBModal: typeof import('bootstrap-vue-next')['vBModal']
vBTooltip: typeof import('bootstrap-vue-next')['vBTooltip']
}
}
23 changes: 23 additions & 0 deletions apps/studio/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" />
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate, max-age=0">
<link rel="icon" type="image/svg+xml" href="/favicon.ico" />
<title>TwinStory</title>
</head>

<body class="overflow-hidden">
<noscript>
<strong>We're sorry but the app doesn't work properly without JavaScript enabled. Please enable it
to continue.</strong>
</noscript>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
<script src="https://kit.fontawesome.com/06b7267748.js" crossorigin="anonymous"></script>
</body>

</html>
9 changes: 9 additions & 0 deletions apps/studio/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "@thxnetwork/studio",
"contributors": [
"Peter Polman <peter@thx.network>"
],
"license": "AGPL-3.0",
"version": "1.0.0",
"dependencies": {}
}
43 changes: 43 additions & 0 deletions apps/studio/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"name": "studio",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/studio/src",
"projectType": "application",
"tags": [],
"targets": {
"build": {
"executor": "@nx/vite:build",
"options": {
"outputPath": "dist/apps/studio"
},
"configurations": {
"production": {
"sourcemap": false,
"minify": true
}
}
},
"serve": {
"executor": "@nx/vite:dev-server",
"defaultConfiguration": "development",
"options": {
"buildTarget": "app:build",
"port": 8085,
"fs": {
"allow": [".."]
}
},
"configurations": {}
},
"lint": {
"executor": "@nx/eslint:lint",
"outputs": ["{options.outputFile}"]
},
"test": {
"executor": "@nxext/vitest:vitest",
"options": {
"vitestConfig": "apps/app/vitest.config.ts"
}
}
}
}
Binary file added apps/studio/public/favicon.ico
Binary file not shown.
15 changes: 15 additions & 0 deletions apps/studio/src/App.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<div class="d-flex bg-primary h-100 overflow-auto flex-column">
<router-view v-slot="{ Component }">
<transition name="fade" mode="out-in">
<component :is="Component" />
</transition>
</router-view>
</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
export default defineComponent({});
</script>
Empty file added apps/studio/src/assets/.gitkeep
Empty file.
Binary file added apps/studio/src/assets/logo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/studio/src/assets/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
Loading

0 comments on commit ba4b4eb

Please sign in to comment.