initial commit
Some checks failed
Build Images and Deploy / Update-PROD-Stack (push) Failing after 17s
Some checks failed
Build Images and Deploy / Update-PROD-Stack (push) Failing after 17s
This commit is contained in:
6
.env.example
Normal file
6
.env.example
Normal file
@@ -0,0 +1,6 @@
|
||||
# Backend
|
||||
PORT=4000
|
||||
JWT_SECRET=your_jwt_secret
|
||||
DATABASE_URL=postgres://postgres:postgres@db:5432/whats_the_point
|
||||
# Frontend
|
||||
VITE_API_URL=http://localhost:4000
|
||||
76
.gitea/workflows/rebuild-prod.yaml
Normal file
76
.gitea/workflows/rebuild-prod.yaml
Normal file
@@ -0,0 +1,76 @@
|
||||
# https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions
|
||||
name: Build Images and Deploy
|
||||
run-name: ${{ gitea.actor }} is building new PROD images and redeploying the existing stack 🚀
|
||||
on:
|
||||
push:
|
||||
# not working right now https://github.com/actions/runner/issues/2324
|
||||
# paths-ignore:
|
||||
# - **.yml
|
||||
branches:
|
||||
- main
|
||||
|
||||
env:
|
||||
STACK_NAME: wtp-prod
|
||||
DOT_ENV: ${{ secrets.PROD_ENV }}
|
||||
PORTAINER_TOKEN: ${{ secrets.PORTAINER_TOKEN }}
|
||||
PORTAINER_API_URL: https://portainer.dev.nervesocket.com/api
|
||||
ENDPOINT_NAME: "mini" #sometimes "primary"
|
||||
IMAGE_TAG: "reg.dev.nervesocket.com/wtp-prod:release"
|
||||
|
||||
jobs:
|
||||
Update-PROD-Stack:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# if: contains(github.event.pull_request.head.ref, 'init-stack')
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Build and push PROD Docker image
|
||||
run: |
|
||||
echo $DOT_ENV | base64 -d > .env
|
||||
echo .env
|
||||
docker buildx build --push -t $IMAGE_TAG .
|
||||
|
||||
- name: Get the endpoint ID
|
||||
# Usually ID is 1, but you can get it from the API. Only skip this if you are VERY sure.
|
||||
run: |
|
||||
ENDPOINT_ID=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/endpoints" | jq -r ".[] | select(.Name==\"$ENDPOINT_NAME\") | .Id")
|
||||
echo "ENDPOINT_ID=$ENDPOINT_ID" >> $GITHUB_ENV
|
||||
echo "Got stack Endpoint ID: $ENDPOINT_ID"
|
||||
|
||||
- name: Fetch stack ID from Portainer
|
||||
run: |
|
||||
STACK_ID=$(curl -s -H "X-API-Key: $PORTAINER_TOKEN" "$PORTAINER_API_URL/stacks" | jq -r ".[] | select(.Name==\"$STACK_NAME\" and .EndpointId==$ENDPOINT_ID) | .Id")
|
||||
|
||||
echo "STACK_ID=$STACK_ID" >> $GITHUB_ENV
|
||||
echo "Got stack ID: $STACK_ID matched with Endpoint ID: $ENDPOINT_ID"
|
||||
|
||||
- name: Redeploy stack in Portainer
|
||||
run: |
|
||||
# Read stack file content
|
||||
STACK_FILE_CONTENT=$(echo "$(<prod-compose.yml )")
|
||||
|
||||
# Prepare JSON payload
|
||||
JSON_PAYLOAD=$(jq -n --arg stackFileContent "$STACK_FILE_CONTENT" --argjson pullImage true \
|
||||
'{stackFileContent: $stackFileContent, pullImage: $pullImage}')
|
||||
|
||||
echo "About to push the following JSON payload:"
|
||||
echo $JSON_PAYLOAD
|
||||
|
||||
# Update stack in Portainer (this redeploys it)
|
||||
DEPLOY_RESPONSE=$(curl -X PUT "$PORTAINER_API_URL/stacks/$STACK_ID?endpointId=$ENDPOINT_ID" \
|
||||
-H "X-API-Key: $PORTAINER_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
--data "$JSON_PAYLOAD")
|
||||
|
||||
echo "Redeployed stack in Portainer. Response:"
|
||||
echo $DEPLOY_RESPONSE
|
||||
|
||||
- name: Status check
|
||||
run: |
|
||||
echo "📋 This job's status is ${{ job.status }}. Make sure you delete the init file to avoid issues."
|
||||
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# Node
|
||||
node_modules/
|
||||
.env
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
# Build
|
||||
build/
|
||||
dist/
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
# Vite
|
||||
frontend/dist/
|
||||
36
README.md
Normal file
36
README.md
Normal file
@@ -0,0 +1,36 @@
|
||||
# What's The Point
|
||||
|
||||
A web app for tracking predictions and points in TV/movie challenges with friends.
|
||||
|
||||
## Features
|
||||
- Register/login with email and password
|
||||
- Create and join challenges for shows/movies
|
||||
- Make and approve predictions
|
||||
- Mobile-first, modern UI
|
||||
|
||||
## Tech Stack
|
||||
- Frontend: React (Vite)
|
||||
- Backend: Node.js (Express)
|
||||
- Database: PostgreSQL
|
||||
- Auth: JWT (email/password)
|
||||
- Dockerized, self-hosted
|
||||
|
||||
## Getting Started
|
||||
|
||||
### Prerequisites
|
||||
- Docker & Docker Compose
|
||||
|
||||
### Setup
|
||||
1. Copy `.env.example` to `.env` and fill in secrets.
|
||||
2. Build and start all services:
|
||||
```sh
|
||||
docker compose up --build
|
||||
```
|
||||
3. Access the frontend at http://localhost:5173
|
||||
4. API runs at http://localhost:4000
|
||||
|
||||
## Deployment
|
||||
- See `prod-compose.yml` for production deployment.
|
||||
|
||||
## License
|
||||
MIT
|
||||
7
backend/Dockerfile
Normal file
7
backend/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
EXPOSE 4000
|
||||
CMD ["npm", "run", "dev"]
|
||||
17
backend/package.json
Normal file
17
backend/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "wtp-backend",
|
||||
"version": "0.1.0",
|
||||
"main": "src/index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "node src/index.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"express": "^4.18.2",
|
||||
"pg": "^8.11.3",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"dotenv": "^16.4.5",
|
||||
"cors": "^2.8.5"
|
||||
}
|
||||
}
|
||||
17
backend/src/index.js
Normal file
17
backend/src/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
import dotenv from 'dotenv';
|
||||
dotenv.config();
|
||||
|
||||
const app = express();
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
|
||||
app.get('/', (req, res) => {
|
||||
res.json({ message: "What's The Point API" });
|
||||
});
|
||||
|
||||
const PORT = process.env.PORT || 4000;
|
||||
app.listen(PORT, () => {
|
||||
console.log(`API running on port ${PORT}`);
|
||||
});
|
||||
42
docker-compose.yml
Normal file
42
docker-compose.yml
Normal file
@@ -0,0 +1,42 @@
|
||||
version: '3.8'
|
||||
services:
|
||||
db:
|
||||
image: postgres:15
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
POSTGRES_DB: whats_the_point
|
||||
volumes:
|
||||
- db_data:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "5432:5432"
|
||||
|
||||
backend:
|
||||
build: ./backend
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
PORT: 4000
|
||||
JWT_SECRET: your_jwt_secret
|
||||
DATABASE_URL: postgres://postgres:postgres@db:5432/whats_the_point
|
||||
depends_on:
|
||||
- db
|
||||
ports:
|
||||
- "4000:4000"
|
||||
volumes:
|
||||
- ./backend:/app
|
||||
|
||||
frontend:
|
||||
build: ./frontend
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
VITE_API_URL: http://localhost:4000
|
||||
depends_on:
|
||||
- backend
|
||||
ports:
|
||||
- "5173:5173"
|
||||
volumes:
|
||||
- ./frontend:/app
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
7
frontend/Dockerfile
Normal file
7
frontend/Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
EXPOSE 5173
|
||||
CMD ["npm", "run", "dev"]
|
||||
12
frontend/index.html
Normal file
12
frontend/index.html
Normal file
@@ -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>What's The Point</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
17
frontend/package.json
Normal file
17
frontend/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "wtp-frontend",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vite": "^5.0.0"
|
||||
}
|
||||
}
|
||||
9
frontend/src/main.jsx
Normal file
9
frontend/src/main.jsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
|
||||
const App = () => <div style={{fontFamily: 'sans-serif', padding: 24}}>
|
||||
<h1>What's The Point</h1>
|
||||
<p>Welcome! The app is running.</p>
|
||||
</div>;
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')).render(<App />);
|
||||
29
prod-compose.yml
Normal file
29
prod-compose.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
services:
|
||||
web:
|
||||
image: reg.dev.nervesocket.com/wtp-prod:release
|
||||
depends_on:
|
||||
- db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- APACHE_LOG_DIR=/var/www/app
|
||||
- TZ=America/Toronto
|
||||
volumes:
|
||||
- /volume1/docker/wtp-prod/production_web:/app/public/storage
|
||||
ports:
|
||||
- 22798:80
|
||||
|
||||
db:
|
||||
# image: mariadb:10.7
|
||||
image: linuxserver/mariadb
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=XLxXDnUvfkTbDzDlEP5Gy8It
|
||||
- TZ=America/Toronto
|
||||
volumes:
|
||||
- /volume1/docker/wtp-prod/db:/config
|
||||
|
||||
adminer:
|
||||
image: adminer
|
||||
restart: unless-stopped
|
||||
ports:
|
||||
- 22582:8080
|
||||
Reference in New Issue
Block a user