diff --git a/.github/workflows/azure-static-web-apps-zealous-stone-0723df810.yml b/.github/workflows/azure-static-web-apps-zealous-stone-0723df810.yml deleted file mode 100644 index 76b8e794..00000000 --- a/.github/workflows/azure-static-web-apps-zealous-stone-0723df810.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Azure Static Web Apps CI/CD - -on: - push: - branches: - - main - pull_request: - types: [opened, synchronize, reopened, closed] - branches: - - main - -jobs: - build_and_deploy_job: - if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') - runs-on: ubuntu-latest - name: Build and Deploy Job - steps: - - uses: actions/checkout@v3 - with: - submodules: true - lfs: false - - name: Build And Deploy - id: builddeploy - uses: Azure/static-web-apps-deploy@v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ZEALOUS_STONE_0723DF810 }} - repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) - action: "upload" - ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### - # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig - app_location: "/frontend" # App source code path - api_location: "" # Api source code path - optional - output_location: "build" # Built app content directory - optional - ###### End of Repository/Build Configurations ###### - - close_pull_request_job: - if: github.event_name == 'pull_request' && github.event.action == 'closed' - runs-on: ubuntu-latest - name: Close Pull Request Job - steps: - - name: Close Pull Request - id: closepullrequest - uses: Azure/static-web-apps-deploy@v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_ZEALOUS_STONE_0723DF810 }} - action: "close" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e0b9954f..9b436428 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,8 +1,12 @@ -name: Test and Build +name: Test and Build Merge Requests on: push: + paths-ignore: + - 'db/**' pull_request: + paths-ignore: + - 'db/**' jobs: test-and-build: @@ -11,15 +15,16 @@ jobs: # Define Node versions to run CI/CD on strategy: matrix: - node-version: [18.x, 20.x, 21.x] + node-version: [21.x] steps: # Checkout project - - uses: actions/checkout@v3 + - name: Checkout repository + uses: actions/checkout@v4 # Setup Node - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + - name: Use Node.js ${{ matrix.node-version }} for frontend + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} diff --git a/.github/workflows/data-upgrade.yml b/.github/workflows/data-upgrade.yml new file mode 100644 index 00000000..e32994f2 --- /dev/null +++ b/.github/workflows/data-upgrade.yml @@ -0,0 +1,38 @@ +name: Convert CSV to JSON for Database updates + +on: + push: + paths: + - 'db/**' + pull_request: + paths: + - 'db/**' + +jobs: + convert: + runs-on: ubuntu-latest + + steps: + # Checkout project + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install pandas + run: pip install pandas + + - name: Install regex + run: pip install regex + + - name: Convert CSV to JSON + run: python db/convert-csv-to-json.py + + - name: Upload JSON files as artifacts + uses: actions/upload-artifact@v4 + with: + name: json-files + path: db/json/ \ No newline at end of file diff --git a/.github/workflows/release-new-data.yml b/.github/workflows/release-new-data.yml new file mode 100644 index 00000000..5bb2fb9f --- /dev/null +++ b/.github/workflows/release-new-data.yml @@ -0,0 +1,73 @@ +name: Convert CSV to JSON for Database updates and release to Azure DB + +on: + push: + paths: + - 'db/**' + branches: + - main + pull_request: + branches: + - main + paths: + - 'db/**' + +jobs: + convert: + runs-on: ubuntu-latest + + steps: + # Checkout project + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Install pandas + run: pip install pandas + + - name: Install regex + run: pip install regex + + - name: Convert CSV to JSON + run: python db/convert-csv-to-json.py + + - name: Upload JSON files as artifacts + uses: actions/upload-artifact@v4 + with: + name: json-files + path: db/json/ + + mongoimport: + needs: convert + runs-on: ubuntu-latest + strategy: + matrix: + mongodb-version: ['6.0'] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download JSON files as artifacts + uses: actions/download-artifact@v4 + with: + name: json-files + path: db/json/ + + - name: Install MongoDB Tools + run: | + wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add - + echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu $(lsb_release -sc)/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list + sudo apt-get update + sudo apt-get install -y mongodb-database-tools + + - name: Import to MongoDB + run: | + chmod +x ./db/import_json_to_mongo.sh + ./db/import_json_to_mongo.sh + shell: bash + env: + MONGODB_URI: ${{ secrets.MONGODB_URI }} \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..20d5c00d --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,105 @@ +name: Build and deploy Express.js backend and React frontend as Azure Web App + +on: + push: + paths-ignore: + - 'db/**' + branches: + - main + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [21.x] + + steps: + # Checkout project + - name: Checkout repository + uses: actions/checkout@v4 + + # Setup Node + - name: Use Node.js ${{ matrix.node-version }} for frontend + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + # FRONTEND # + # Install Packages + - name: Install Dependencies for Frontend + run: npm ci + working-directory: frontend + + # Include whenver tests are added + # - name: npm run test for frontend + # run: npm run test --if-present + # working-directory: frontend + + - name: npm run build for frontend + run: npm run build + working-directory: frontend + + # working-directory: frontend + # with: + # node-version: '18.x' + # app_location: "/frontend" + # output_location: "build" + + # Copy build directory to backend + - name: Copy frontend build to backend + run: cp -R ./frontend/build/* ./backend/__BUILD/ + + # BACKEND # + # Install Packages + - name: Install Dependencies for Backend + run: | + npm ci + working-directory: ./backend + + # Run Jest tests + - name: Run Tests + working-directory: ./backend + run: npm run test --if-present + + # Compile TS + - name: Compile TS + working-directory: ./backend + run: npm run compile + + - name: Zip artifact for deployment + run: zip release.zip ./* -r + working-directory: ./backend + + - name: Upload artifact for deployment job + uses: actions/upload-artifact@v3 + with: + name: node-app + path: backend/release.zip + + deploy: + runs-on: ubuntu-latest + needs: build + environment: + name: 'Production' + url: ${{ steps.deploy-to-webapp.outputs.webapp-url }} + + steps: + - name: Download artifact from build job + uses: actions/download-artifact@v3 + with: + name: node-app + + - name: Unzip artifact for deployment + run: unzip release.zip + + - name: 'Deploy to Azure Web App' + id: deploy-to-webapp + uses: azure/webapps-deploy@v2 + with: + app-name: 'lotr-backend' + slot-name: 'Production' + package: . + publish-profile: ${{ secrets.AZUREAPPSERVICE_PUBLISHPROFILE_755D03D3D66E4CFEA1B7360C17082E2D }} diff --git a/.gitignore b/.gitignore index 9b3f0442..d0435b00 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ db/bson/user.bson db/bson/user.metadata.json db/init-prod.js db/db +db/bson-for-azure sample-app/node_modules docker-compose.yml .idea diff --git a/backend/.gitignore b/backend/.gitignore index d09c770e..1dcef2d9 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -1,3 +1,2 @@ node_modules -__BUILD .env \ No newline at end of file diff --git a/backend/__BUILD/test.txt b/backend/__BUILD/test.txt new file mode 100644 index 00000000..e69de29b diff --git a/backend/package.json b/backend/package.json index f7caaf2e..0771c7ca 100644 --- a/backend/package.json +++ b/backend/package.json @@ -7,8 +7,9 @@ "test": "jest", "compile": "npx tsc", "format": "prettier --write \"**/*.ts\"", - "prod": "NODE_ENV=production node server.js", - "prod_win": "set NODE_ENV=production&&node server.js" + "prod": "NODE_ENV=production node dist/server.js", + "prod_win": "set NODE_ENV=production&&node dist/server.js", + "start": "NODE_ENV=production node dist/server.js" }, "keywords": [ "the lord of the rings", @@ -67,4 +68,4 @@ "ts-node": "^10.9.1", "typescript": "^5.3.2" } -} +} \ No newline at end of file diff --git a/backend/server.ts b/backend/server.ts index 15cb27fb..7af6d723 100644 --- a/backend/server.ts +++ b/backend/server.ts @@ -27,7 +27,7 @@ app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use(cors()); -app.use(express.static(path.join(__dirname, '/__BUILD'))); // React build +app.use(express.static(path.join(__dirname, '/../__BUILD'))); // React build const server_port = process.env.PORT || 3001; @@ -100,13 +100,13 @@ app.use((req, res, next) => { } }); -app.use('/v2/', apiLimiter); +app.use('/v2/', apiLimiter); app.use('/v2', apiRoutes); app.use('/auth', authRoutes); // Handles React frontend requests app.get('*', (req, res) => { - res.sendFile(path.join(__dirname + '/__BUILD/index.html')); + res.sendFile(path.join(__dirname + '/../__BUILD/index.html')); }); async function start() { diff --git a/db/convert-csv-to-json.py b/db/convert-csv-to-json.py new file mode 100644 index 00000000..327e6dda --- /dev/null +++ b/db/convert-csv-to-json.py @@ -0,0 +1,32 @@ +import os +import pandas as pd +import json +import regex as re + +def transform_objectid(text): + """Replace MongoDB ObjectId references to proper JSON format.""" + # Use non-capturing group and directly format the string with $oid. + pattern = r'ObjectId\(([^)]+)\)' + replacements = re.findall(pattern, text) + for r in replacements: + text = text.replace(f'ObjectId({r})', f'{{"$oid": "{r}"}}') + return text + +def main(): + os.makedirs('db/json', exist_ok=True) # Ensure the directory for JSON files exists + csv_files = [f for f in os.listdir('db/csv') if f.endswith('.csv')] + + for file in csv_files: + df = pd.read_csv(f'db/csv/{file}') + # Transform all string columns that may contain ObjectId references + for column in df.select_dtypes(include=['object']): + df[column] = df[column].apply(lambda x: transform_objectid(str(x)) if pd.notna(x) else x) + # Convert transformed string JSON to actual JSON objects + for column in df.select_dtypes(include=['object']): + df[column] = df[column].apply(lambda x: json.loads(x) if pd.notna(x) and x.startswith('{') else x) + # Save each dataframe as a JSON file with all objects in a single array + json_path = f'db/json/{file.replace(".csv", ".json")}' + df.to_json(json_path, orient='records', indent=4) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/db/csv/quotes.csv b/db/csv/quotes.csv index 22db7e72..5295bd6f 100644 --- a/db/csv/quotes.csv +++ b/db/csv/quotes.csv @@ -1,5 +1,5 @@ dialog,movie,character,_id -Deagol!,ObjectId(5cd95395de30eff6ebccde5d),ObjectId(5cd99d4bde30eff6ebccfe9e),ObjectId(5cd96e05de30eff6ebcce7e9) +Deagol!!,ObjectId(5cd95395de30eff6ebccde5d),ObjectId(5cd99d4bde30eff6ebccfe9e),ObjectId(5cd96e05de30eff6ebcce7e9) Deagol!,ObjectId(5cd95395de30eff6ebccde5d),ObjectId(5cd99d4bde30eff6ebccfe9e),ObjectId(5cd96e05de30eff6ebcce7ea) Deagol!,ObjectId(5cd95395de30eff6ebccde5d),ObjectId(5cd99d4bde30eff6ebccfe9e),ObjectId(5cd96e05de30eff6ebcce7eb) Give us that! Deagol my love,ObjectId(5cd95395de30eff6ebccde5d),ObjectId(5cd99d4bde30eff6ebccfe9e),ObjectId(5cd96e05de30eff6ebcce7ec) diff --git a/db/import_json_to_mongo.sh b/db/import_json_to_mongo.sh new file mode 100644 index 00000000..204bc519 --- /dev/null +++ b/db/import_json_to_mongo.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# Script to import JSON files to MongoDB collections +for file in db/json/*.json; do + # Extract the collection name from the filename + collection=$(basename "$file" .json) + echo "Importing $file to collection $collection" + # Run mongoimport command + mongoimport --type json --uri "$MONGODB_URI" --collection $collection --file "$file" --drop --maintainInsertionOrder --jsonArray +done \ No newline at end of file diff --git a/frontend/src/components/Footer.tsx b/frontend/src/components/Footer.tsx index e6887b40..420a54ef 100644 --- a/frontend/src/components/Footer.tsx +++ b/frontend/src/components/Footer.tsx @@ -6,7 +6,7 @@ const Footer: React.FC = () => {
All we have to decide is what to do with the time that is given to us.{" "}
- Built with ♥ in 2020
+ Built with ♥ 2019-{new Date().getFullYear()}
- The one API to rule them all is a project by{" "}
+ The one API to rule them all is a non-commercial,
+ open-source project maintained by{" "}
Rike
{" "}
- started during the{" "}
+ and{" "}
- #100DaysOfCode challenge
- {" "}
- and is brought to you via{" "}
-
- DigitalOcean
+ Mateusz
- . You have some questions or suggestions? Find me on Twitter (
+ , which has been around since 2019. If you have any quick questions or
+ suggestions, feel free to reach out to Rike on X (
{
Express.js
{" "}
-{" "}
+
+ Azure
+ {" "}
+ -{" "}
{
{" "}
and more.
- It could not have been made without the scraping work of Moko Sharma
- (
+ This project would not have been possible without the invaluable
+ scraping contributions of Moko Sharma (
{
>
Kaggle
- ) .
+ ).
Have a look into some great projects based on this API. You would like to get listed?{" "} - Drop me a line. + Drop Rike a line.
+ + Not sure how to do this? Please refer to the documentation's{" "} + auth part! + +
diff --git a/frontend/src/pages/Documentation.tsx b/frontend/src/pages/Documentation.tsx index a77f5e92..c0e02005 100644 --- a/frontend/src/pages/Documentation.tsx +++ b/frontend/src/pages/Documentation.tsx @@ -12,10 +12,15 @@ const Documentation: React.FC = () => { What is an API?- Yes, I wrote a blog post that explains in detail how REST APIs work and why you would use - them at all. It also covers authentication, JSON handling and a sample React app. {' '} - + Yes, I wrote a blog post that explains in detail how REST APIs work + and why you would use them at all. It also covers authentication, + JSON handling and a sample React app.{" "} + This is the link to the blog post - and + {" "} + and{" "} + here you will find the according code base - for the - sample React app. + {" "} + for the sample React app.
Endpoint | -Response | -Token required | +Endpoint | +Response | +Token required | Request all chapters of one specific book | -+ | |||
---|---|---|---|---|---|---|---|---|---|---|
@@ -231,8 +258,8 @@ const Documentation: React.FC = () => {
|