Skip to content

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
elliotBraem committed Dec 17, 2024
1 parent 24b41d3 commit b5cef68
Show file tree
Hide file tree
Showing 46 changed files with 678 additions and 72 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,5 @@ next-env.d.ts
/playwright/.cache/

.cache
.db
.db
.turbo
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file added backend/bun.lockb
Binary file not shown.
7 changes: 7 additions & 0 deletions backend/next.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
* Increase time
* Write README
* Test with a new submission and approval
* Test with reject
* Integrate NEAR contract
* Build a small dashboard to see what there is to be approved and rejected?
* Tag all of the admins in the "Received" reply
47 changes: 47 additions & 0 deletions backend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"name": "backend",
"version": "0.0.1",
"packageManager": "[email protected]",
"scripts": {
"build": "bun build ./src/index.ts --outdir=dist",
"start": "bun run dist/index.js",
"dev": "bun run --watch src/index.ts",
"test": "bun test",
"fmt": "prettier --write '**/*.{js,jsx,ts,tsx,json}'",
"fmt:check": "prettier --check '**/*.{js,jsx,ts,tsx,json}'"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@types/express": "^4.17.17",
"@types/jest": "^29.5.11",
"@types/node": "^20.10.6",
"@types/ora": "^3.2.0",
"jest": "^29.7.0",
"prettier": "^3.3.3",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typescript": "^5.3.3"
},
"dependencies": {
"agent-twitter-client": "^0.0.16",
"cors": "^2.8.5",
"@types/cors": "^2.8.17",
"dotenv": "^16.0.3",
"express": "^4.18.2",
"near-api-js": "^2.1.4",
"ora": "^8.1.1",
"winston": "^3.17.0",
"winston-console-format": "^1.0.8"
}
}
File renamed without changes.
File renamed without changes.
58 changes: 57 additions & 1 deletion src/index.ts → backend/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import dotenv from "dotenv";
import express from "express";
import cors from "cors";
import path from "path";
import { TwitterService } from "./services/twitter/client";
import { NearService } from "./services/near";
import { db } from "./services/db";
import config from "./config/config";
import {
logger,
Expand All @@ -10,6 +14,52 @@ import {
cleanup
} from "./utils/logger";

// Initialize Express
const app = express();
const PORT = process.env.PORT || 3000;

// Middleware
app.use(cors());
app.use(express.json());

// Serve static frontend files in production
if (process.env.NODE_ENV === 'production') {
app.use(express.static(path.join(__dirname, '../frontend/dist')));
}

// API Routes
app.get('/api/submissions', (req, res) => {
try {
const status = req.query.status as "pending" | "approved" | "rejected";
const submissions = status ?
db.getSubmissionsByStatus(status) :
db.getAllSubmissions();
res.json(submissions);
} catch (error) {
res.status(500).json({ error: 'Failed to fetch submissions' });
}
});

app.get('/api/submissions/:tweetId', (req, res) => {
try {
const submission = db.getSubmission(req.params.tweetId);
if (!submission) {
res.status(404).json({ error: 'Submission not found' });
return;
}
res.json(submission);
} catch (error) {
res.status(500).json({ error: 'Failed to fetch submission' });
}
});

// Serve frontend for all other routes in production
if (process.env.NODE_ENV === 'production') {
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../frontend/dist/index.html'));
});
}

async function main() {
try {
// Load environment variables
Expand All @@ -28,6 +78,12 @@ async function main() {
await twitterService.initialize();
succeedSpinner('twitter-init', 'Twitter service initialized');

// Start Express server
startSpinner('express', 'Starting Express server...');
app.listen(PORT, () => {
succeedSpinner('express', `Express server running on port ${PORT}`);
});

// Handle graceful shutdown
process.on("SIGINT", async () => {
startSpinner('shutdown', 'Shutting down gracefully...');
Expand All @@ -54,7 +110,7 @@ async function main() {

} catch (error) {
// Handle any initialization errors
['env', 'near', 'twitter-init', 'twitter-mentions'].forEach(key => {
['env', 'near', 'twitter-init', 'twitter-mentions', 'express'].forEach(key => {
failSpinner(key, `Failed during ${key}`);
});
logger.error('Startup', error);
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -209,43 +209,62 @@ export class TwitterService {
const userId = tweet.userId;
if (!userId || !tweet.id) return;

// Get submission count from database instead of memory
const dailyCount = db.getDailySubmissionCount(userId);

if (dailyCount >= this.DAILY_SUBMISSION_LIMIT) {
await this.replyToTweet(
tweet.id,
"You've reached your daily submission limit. Please try again tomorrow."
);
logger.info(`User ${userId} has reached limit, replied to submission.`);
// Get the tweet being replied to
const inReplyToId = tweet.inReplyToStatusId;
if (!inReplyToId) {
logger.error(`Submission tweet ${tweet.id} is not a reply to another tweet`);
return;
}

const submission: TwitterSubmission = {
tweetId: tweet.id,
userId: userId,
content: tweet.text || "",
hashtags: tweet.hashtags || [],
status: "pending",
moderationHistory: [],
};
try {
// Fetch the original tweet that's being submitted
const originalTweet = await this.client.getTweet(inReplyToId);
if (!originalTweet) {
logger.error(`Could not fetch original tweet ${inReplyToId}`);
return;
}

// Save submission to database
db.saveSubmission(submission);
// Increment submission count in database
db.incrementDailySubmissionCount(userId);
// Get submission count from database
const dailyCount = db.getDailySubmissionCount(userId);

// Send acknowledgment and save its ID
const acknowledgmentTweetId = await this.replyToTweet(
tweet.id,
"Successfully submitted to publicgoods.news!"
);

if (acknowledgmentTweetId) {
db.updateSubmissionAcknowledgment(tweet.id, acknowledgmentTweetId);
logger.info(`Successfully submitted. Sent reply: ${this.getTweetLink(acknowledgmentTweetId)}`)
} else {
logger.error(`Failed to acknowledge submission: ${this.getTweetLink(tweet.id, tweet.username)}`)
if (dailyCount >= this.DAILY_SUBMISSION_LIMIT) {
await this.replyToTweet(
tweet.id,
"You've reached your daily submission limit. Please try again tomorrow."
);
logger.info(`User ${userId} has reached limit, replied to submission.`);
return;
}

// Create submission using the original tweet's content
const submission: TwitterSubmission = {
tweetId: originalTweet.id!, // The tweet being submitted
userId: userId, // The user who submitted it
content: originalTweet.text || "",
hashtags: originalTweet.hashtags || [],
status: "pending",
moderationHistory: [],
};

// Save submission to database
db.saveSubmission(submission);
// Increment submission count in database
db.incrementDailySubmissionCount(userId);

// Send acknowledgment and save its ID
const acknowledgmentTweetId = await this.replyToTweet(
tweet.id, // Reply to the submission tweet
"Successfully submitted to publicgoods.news!"
);

if (acknowledgmentTweetId) {
db.updateSubmissionAcknowledgment(originalTweet.id!, acknowledgmentTweetId);
logger.info(`Successfully submitted. Sent reply: ${this.getTweetLink(acknowledgmentTweetId)}`)
} else {
logger.error(`Failed to acknowledge submission: ${this.getTweetLink(tweet.id, tweet.username)}`)
}
} catch (error) {
logger.error(`Error handling submission for tweet ${tweet.id}:`, error);
}
}

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file modified bun.lockb
Binary file not shown.
24 changes: 24 additions & 0 deletions frontend/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*

node_modules
dist
dist-ssr
*.local

# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
50 changes: 50 additions & 0 deletions frontend/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh

## Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:

- Configure the top-level `parserOptions` property like this:

```js
export default tseslint.config({
languageOptions: {
// other options...
parserOptions: {
project: ['./tsconfig.node.json', './tsconfig.app.json'],
tsconfigRootDir: import.meta.dirname,
},
},
})
```

- Replace `tseslint.configs.recommended` to `tseslint.configs.recommendedTypeChecked` or `tseslint.configs.strictTypeChecked`
- Optionally add `...tseslint.configs.stylisticTypeChecked`
- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and update the config:

```js
// eslint.config.js
import react from 'eslint-plugin-react'

export default tseslint.config({
// Set the react version
settings: { react: { version: '18.3' } },
plugins: {
// Add the react plugin
react,
},
rules: {
// other rules...
// Enable its recommended rules
...react.configs.recommended.rules,
...react.configs['jsx-runtime'].rules,
},
})
```
Binary file added frontend/bun.lockb
Binary file not shown.
28 changes: 28 additions & 0 deletions frontend/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'

export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)
12 changes: 12 additions & 0 deletions frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Curation Dashboard</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
35 changes: 35 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "frontend",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --port 5173",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview"
},
"dependencies": {
"@tailwindcss/typography": "^0.5.15",
"autoprefixer": "^10.4.20",
"axios": "^1.7.9",
"postcss": "^8.4.49",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^7.0.2",
"tailwindcss": "^3.4.16"
},
"devDependencies": {
"@eslint/js": "^9.15.0",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.4",
"eslint": "^9.15.0",
"eslint-plugin-react-hooks": "^5.0.0",
"eslint-plugin-react-refresh": "^0.4.14",
"globals": "^15.12.0",
"typescript": "~5.6.2",
"typescript-eslint": "^8.15.0",
"vite": "^6.0.1"
}
}
Loading

0 comments on commit b5cef68

Please sign in to comment.