mirror of
https://github.com/origranot/reduced.to.git
synced 2025-03-14 10:33:54 +00:00
feat: refactor to nx
This commit is contained in:
@ -1,15 +1,13 @@
|
||||
# https://editorconfig.org
|
||||
# Editor configuration, see http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
max_line_length = 80
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
max_line_length = 0
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
|
3
.eslintignore
Normal file
3
.eslintignore
Normal file
@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
|
||||
vite.config.ts
|
@ -1,9 +0,0 @@
|
||||
{
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
"parserOptions": { "ecmaVersion": 2018, "sourceType": "module" },
|
||||
"rules": {
|
||||
"max-len": ["error", { "code": 130 }]
|
||||
}
|
||||
}
|
35
.eslintrc.json
Normal file
35
.eslintrc.json
Normal file
@ -0,0 +1,35 @@
|
||||
{
|
||||
"root": true,
|
||||
"ignorePatterns": ["**/*"],
|
||||
"plugins": ["@nx"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {
|
||||
"@nx/enforce-module-boundaries": [
|
||||
"error",
|
||||
{
|
||||
"enforceBuildableLibDependency": true,
|
||||
"allow": [],
|
||||
"depConstraints": [
|
||||
{
|
||||
"sourceTag": "*",
|
||||
"onlyDependOnLibsWithTags": ["*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"extends": ["plugin:@nx/typescript"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"extends": ["plugin:@nx/javascript"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
39
.github/workflows/build.yml
vendored
39
.github/workflows/build.yml
vendored
@ -1,39 +0,0 @@
|
||||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- name: Cache Node Modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: Install NPM Dependencies
|
||||
run: npm install && npm run install:all
|
||||
|
||||
- name: Build solution
|
||||
if: always()
|
||||
run: npm run build:all
|
29
.github/workflows/ci.yml
vendored
Normal file
29
.github/workflows/ci.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
main:
|
||||
name: Continuous Integration (CI)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: 'read'
|
||||
actions: 'read'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: nrwl/nx-set-shas@v3
|
||||
with:
|
||||
main-branch-name: 'master'
|
||||
|
||||
- run: npm ci
|
||||
|
||||
- run: npx nx format:check
|
||||
- run: npx nx affected -t lint --parallel=3
|
||||
- run: npx nx affected -t test --parallel=3
|
||||
- run: npx nx affected -t build --parallel=3
|
73
.github/workflows/codeql.yml
vendored
73
.github/workflows/codeql.yml
vendored
@ -1,73 +0,0 @@
|
||||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: 'CodeQL'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['master']
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: ['master']
|
||||
schedule:
|
||||
- cron: '31 11 * * 0'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: ['javascript']
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
|
||||
# Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
|
||||
# queries: security-extended,security-and-quality
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: '/language:${{matrix.language}}'
|
97
.github/workflows/deploy.yml
vendored
97
.github/workflows/deploy.yml
vendored
@ -5,75 +5,34 @@ on:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
workflow_run:
|
||||
workflows: [CI]
|
||||
types:
|
||||
- completed
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
IMAGE_NAME_BACKEND: ${{ github.repository }}/backend
|
||||
IMAGE_NAME_FRONTEND: ${{ github.repository }}/frontend
|
||||
NX_AFFECTED: true
|
||||
NX_AFFECTED_BASE: origin/master
|
||||
|
||||
jobs:
|
||||
backend-deployment:
|
||||
name: Build and publish backend image
|
||||
affected-deployment:
|
||||
name: Build and publish affected images
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
permissions: 'write-all'
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v1
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Install npm dependencies
|
||||
- uses: nrwl/nx-set-shas@v3
|
||||
with:
|
||||
main-branch-name: 'master'
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v1
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_BACKEND }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./apps/backend
|
||||
file: ./apps/backend/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
frontend-deployment:
|
||||
name: Build and publish frontend image
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
@ -81,23 +40,7 @@ jobs:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v1
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME_FRONTEND }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
with:
|
||||
context: ./apps/frontend
|
||||
file: ./apps/frontend/Dockerfile.prod
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
build-args: |
|
||||
API_DOMAIN=${{ secrets.API_DOMAIN }}
|
||||
DOMAIN=${{ secrets.DOMAIN }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
- name: Build & Push Affected Images to Registry
|
||||
id: deploy-images-to-registry
|
||||
run: |
|
||||
npx nx affected -t "push-image-to-registry" --repository=${{ github.repository }} --github-sha="$GITHUB_SHA"
|
||||
|
39
.github/workflows/lint.yml
vendored
39
.github/workflows/lint.yml
vendored
@ -1,39 +0,0 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- name: Cache Node Modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: Install NPM Dependencies
|
||||
run: npm install && npm run install:all
|
||||
|
||||
- name: Prettier Check
|
||||
if: always()
|
||||
run: npm run fmt.check
|
39
.github/workflows/test.yml
vendored
39
.github/workflows/test.yml
vendored
@ -1,39 +0,0 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16.x
|
||||
registry-url: https://registry.npmjs.org/
|
||||
|
||||
- name: Cache Node Modules
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: Install NPM Dependencies
|
||||
run: npm install && npm run install:all
|
||||
|
||||
- name: Run tests
|
||||
if: always()
|
||||
run: npm run test:all
|
47
.gitignore
vendored
47
.gitignore
vendored
@ -1,21 +1,42 @@
|
||||
# Coverage reports
|
||||
coverage
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# API keys and secrets
|
||||
# Secrets
|
||||
.env
|
||||
|
||||
# Dependency directory
|
||||
# compiled output
|
||||
dist
|
||||
tmp
|
||||
/out-tsc
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
bower_components
|
||||
|
||||
# Editors
|
||||
.idea
|
||||
*.iml
|
||||
.vscode
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# OS metadata
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
# Ignore built ts files
|
||||
dist/
|
@ -1,4 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install lint-staged
|
@ -1,10 +1,3 @@
|
||||
# Files Prettier should not format
|
||||
**/*.log
|
||||
**/.DS_Store
|
||||
*.
|
||||
**/dist
|
||||
**/node_modules
|
||||
|
||||
# Ignore all build directories on frontend
|
||||
apps/frontend/server
|
||||
|
||||
# Add files here to ignore them from prettier formatting
|
||||
/dist
|
||||
/coverage
|
4
.prettierrc
Normal file
4
.prettierrc
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"printWidth": 140
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"useTabs": false,
|
||||
"printWidth": 130
|
||||
}
|
8
.vscode/extensions.json
vendored
Normal file
8
.vscode/extensions.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"nrwl.angular-console",
|
||||
"esbenp.prettier-vscode",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"firsttris.vscode-jest-runner"
|
||||
]
|
||||
}
|
@ -1,131 +0,0 @@
|
||||
# Contributor Covenant Code of Conduct
|
||||
|
||||
## Our Pledge
|
||||
|
||||
We as members, contributors, and leaders pledge to make participation in our
|
||||
community a harassment-free experience for everyone, regardless of age, body
|
||||
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||
identity and expression, level of experience, education, socio-economic status,
|
||||
nationality, personal appearance, race, religion, or sexual identity
|
||||
and orientation.
|
||||
|
||||
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||
diverse, inclusive, and healthy community.
|
||||
|
||||
## Our Standards
|
||||
|
||||
Examples of behavior that contributes to a positive environment for our
|
||||
community include:
|
||||
|
||||
- Demonstrating empathy and kindness toward other people
|
||||
- Being respectful of differing opinions, viewpoints, and experiences
|
||||
- Giving and gracefully accepting constructive feedback
|
||||
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||
and learning from the experience
|
||||
- Focusing on what is best not just for us as individuals, but for the
|
||||
overall community
|
||||
|
||||
Examples of unacceptable behavior include:
|
||||
|
||||
- The use of sexualized language or imagery, and sexual attention or
|
||||
advances of any kind
|
||||
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||
- Public or private harassment
|
||||
- Publishing others' private information, such as a physical or email
|
||||
address, without their explicit permission
|
||||
- Other conduct which could reasonably be considered inappropriate in a
|
||||
professional setting
|
||||
|
||||
## Enforcement Responsibilities
|
||||
|
||||
Community leaders are responsible for clarifying and enforcing our standards of
|
||||
acceptable behavior and will take appropriate and fair corrective action in
|
||||
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||
or harmful.
|
||||
|
||||
Community leaders have the right and responsibility to remove, edit, or reject
|
||||
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||
decisions when appropriate.
|
||||
|
||||
## Scope
|
||||
|
||||
This Code of Conduct applies within all community spaces, and also applies when
|
||||
an individual is officially representing the community in public spaces.
|
||||
Examples of representing our community include using an official e-mail address,
|
||||
posting via an official social media account, or acting as an appointed
|
||||
representative at an online or offline event.
|
||||
|
||||
## Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||
reported to the community leaders responsible for enforcement.
|
||||
All complaints will be reviewed and investigated promptly and fairly.
|
||||
|
||||
All community leaders are obligated to respect the privacy and security of the
|
||||
reporter of any incident.
|
||||
|
||||
## Enforcement Guidelines
|
||||
|
||||
Community leaders will follow these Community Impact Guidelines in determining
|
||||
the consequences for any action they deem in violation of this Code of Conduct:
|
||||
|
||||
### 1. Correction
|
||||
|
||||
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||
unprofessional or unwelcome in the community.
|
||||
|
||||
**Consequence**: A private, written warning from community leaders, providing
|
||||
clarity around the nature of the violation and an explanation of why the
|
||||
behavior was inappropriate. A public apology may be requested.
|
||||
|
||||
### 2. Warning
|
||||
|
||||
**Community Impact**: A violation through a single incident or series
|
||||
of actions.
|
||||
|
||||
**Consequence**: A warning with consequences for continued behavior. No
|
||||
interaction with the people involved, including unsolicited interaction with
|
||||
those enforcing the Code of Conduct, for a specified period of time. This
|
||||
includes avoiding interactions in community spaces as well as external channels
|
||||
like social media. Violating these terms may lead to a temporary or
|
||||
permanent ban.
|
||||
|
||||
### 3. Temporary Ban
|
||||
|
||||
**Community Impact**: A serious violation of community standards, including
|
||||
sustained inappropriate behavior.
|
||||
|
||||
**Consequence**: A temporary ban from any sort of interaction or public
|
||||
communication with the community for a specified period of time. No public or
|
||||
private interaction with the people involved, including unsolicited interaction
|
||||
with those enforcing the Code of Conduct, is allowed during this period.
|
||||
Violating these terms may lead to a permanent ban.
|
||||
|
||||
### 4. Permanent Ban
|
||||
|
||||
**Community Impact**: Demonstrating a pattern of violation of community
|
||||
standards, including sustained inappropriate behavior, harassment of an
|
||||
individual, or aggression toward or disparagement of classes of individuals.
|
||||
|
||||
**Consequence**: A permanent ban from any sort of public interaction within
|
||||
the community.
|
||||
|
||||
## Attribution
|
||||
|
||||
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||
version 2.1, available at
|
||||
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||
|
||||
Community Impact Guidelines were inspired by
|
||||
[Mozilla's code of conduct enforcement ladder][mozilla coc].
|
||||
|
||||
For answers to common questions about this code of conduct, see the FAQ at
|
||||
[https://www.contributor-covenant.org/faq][faq]. Translations are available at
|
||||
[https://www.contributor-covenant.org/translations][translations].
|
||||
|
||||
[homepage]: https://www.contributor-covenant.org
|
||||
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||
[mozilla coc]: https://github.com/mozilla/diversity
|
||||
[faq]: https://www.contributor-covenant.org/faq
|
||||
[translations]: https://www.contributor-covenant.org/translations
|
104
CONTRIBUTING.MD
104
CONTRIBUTING.MD
@ -1,104 +0,0 @@
|
||||
# Contributing Guidelines
|
||||
|
||||
## Opening an issue:
|
||||
|
||||
Thank you for taking the time to open an issue.
|
||||
<br>
|
||||
Before opening an issue, please be sure that your issue hasn't already been asked by someone.
|
||||
<br><br>
|
||||
Here are a few things that will help us resolve your issues:
|
||||
|
||||
- A descriptive title that gives an idea of what your issue refers to
|
||||
- A thorough description of the issue, (one word descriptions are very hard to understand)
|
||||
- Screenshots (if appropriate)
|
||||
- Links (if appropriate)
|
||||
<br><br>
|
||||
|
||||
## Submitting a pull request:
|
||||
|
||||
Follow the below mentioned steps to open a pull request(PR):
|
||||
|
||||
#### If you don't have git on your machine, [install it](https://help.github.com/articles/set-up-git/).
|
||||
|
||||
## Fork this repository
|
||||
|
||||
Fork this repository by clicking on the fork button at the top of this page.
|
||||
This will create a copy of this repository in your account.
|
||||
|
||||
## Clone the repository
|
||||
|
||||
Now clone the forked repository to your machine. Go to your GitHub account, open the forked repository, click on the code button and then click the _copy to clipboard_ icon.
|
||||
|
||||
Open a terminal and run the following git command:
|
||||
|
||||
```
|
||||
git clone "url you just copied"
|
||||
```
|
||||
|
||||
where "url you just copied" (without the quotation marks) is the url to this repository (your fork of this project). See the previous steps to obtain the url.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
git clone https://github.com/this-is-you/reduced.to.git
|
||||
```
|
||||
|
||||
where `this-is-you` is your GitHub username. Here you're copying the contents of the first-contributions repository on GitHub to your computer.
|
||||
|
||||
## Create a branch
|
||||
|
||||
Change to the repository directory on your computer (if you are not already there):
|
||||
|
||||
```
|
||||
cd reduced.to
|
||||
```
|
||||
|
||||
Now create a branch using the `git switch` command:
|
||||
|
||||
```
|
||||
git switch -c your-new-branch-name
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
git switch -c improved-ui
|
||||
```
|
||||
|
||||
## Make necessary changes and commit those changes
|
||||
|
||||
If you go to the project directory and execute the command `git status`, you'll see there are changes.
|
||||
|
||||
Add those changes to the branch you just created using the `git add` command:
|
||||
|
||||
```
|
||||
git add "filename with extension in which you have made changes"
|
||||
```
|
||||
|
||||
Now commit those changes using the `git commit` command:
|
||||
|
||||
```
|
||||
git commit -m "Add relevant message to the change you made"
|
||||
```
|
||||
|
||||
## Push changes to GitHub
|
||||
|
||||
Push your changes using the command `git push`:
|
||||
|
||||
```
|
||||
git push origin -u your-branch-name
|
||||
```
|
||||
|
||||
replacing `your-branch-name` with the name of the branch you created earlier.
|
||||
|
||||
## Submit your changes for review
|
||||
|
||||
If you go to your repository on GitHub, you'll see a `Compare & pull request` button. Click on that button.
|
||||
|
||||
Now submit the pull request.
|
||||
|
||||
Soon a reviewer will be merging all your changes into the main branch of this project. You will get a notification email once the changes have been merged.
|
||||
|
||||
## Note:
|
||||
|
||||
PRs that exclusively add items to the list of domains will be classed as spam/invalid. We already grab this list from ICANN's complete list of certified registrars.
|
22
LICENSE
22
LICENSE
@ -1,22 +0,0 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2022-2023 Ori Granot
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -13,7 +13,7 @@ LOGGER_CONSOLE_THRESHOLD=INFO # DEBUG, INFO, WARN, ERROR, FATAL
|
||||
FRONT_DOMAIN=http://localhost:5173
|
||||
|
||||
# DATABASE
|
||||
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/reduced_to_db?schema=public"
|
||||
DATABASE_URL="postgresql://postgres:postgres@postgres:5432/reduced_to_db?schema=public"
|
||||
|
||||
# REDIS
|
||||
REDIS_ENABLE=false
|
||||
@ -27,4 +27,4 @@ JWT_ACCESS_SECRET=abc1234
|
||||
JWT_REFRESH_SECRET=abc1234
|
||||
|
||||
# NOVU - You don't need this when running locally
|
||||
NOVU_API_KEY= Get it from https://novu.co/
|
||||
NOVU_API_KEY= Get it from https://novu.co/
|
18
apps/backend/.eslintrc.json
Normal file
18
apps/backend/.eslintrc.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"extends": ["../../.eslintrc.json"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
41
apps/backend/.gitignore
vendored
41
apps/backend/.gitignore
vendored
@ -1,41 +0,0 @@
|
||||
# Build
|
||||
/dist
|
||||
/lib
|
||||
/lib-types
|
||||
/server
|
||||
|
||||
# Development
|
||||
node_modules
|
||||
|
||||
# Cache
|
||||
.cache
|
||||
.mf
|
||||
.vscode
|
||||
.rollup.cache
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Editor
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Yarn
|
||||
.yarn/*
|
||||
!.yarn/releases
|
||||
|
||||
# env
|
||||
.env
|
@ -1,36 +1,43 @@
|
||||
FROM node:18 AS build
|
||||
# --------------------------------------------
|
||||
# Dependencies Stage
|
||||
# --------------------------------------------
|
||||
FROM node:19.2-alpine3.15 as dependencies
|
||||
|
||||
# Required for Prisma Client to work in container
|
||||
RUN apt-get update && apt-get install -y openssl
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
|
||||
WORKDIR /svr/app
|
||||
RUN apk add --update python3 make g++\
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
COPY --chown=node:node package*.json ./
|
||||
COPY --chown=node:node prisma ./prisma/
|
||||
|
||||
# Install npm dependencies
|
||||
RUN npm ci
|
||||
|
||||
COPY --chown=node:node . .
|
||||
# --------------------------------------------
|
||||
# Build Stage
|
||||
# --------------------------------------------
|
||||
# Intermediate docker image to build the bundle in and install dependencies
|
||||
FROM node:19.2-alpine3.15 as build
|
||||
WORKDIR /app
|
||||
|
||||
# Generate prisma files
|
||||
RUN npx prisma generate
|
||||
COPY . .
|
||||
COPY --from=dependencies /app/node_modules ./node_modules
|
||||
|
||||
# Run the build command which creates the production bundle
|
||||
RUN npm run build
|
||||
# Run prisma generate & build the bundle in production mode
|
||||
RUN npx nx build backend --prod --skip-nx-cache
|
||||
|
||||
# Set NODE_ENV environment variable
|
||||
ENV NODE_ENV production
|
||||
|
||||
FROM node:18-alpine3.16 AS production
|
||||
# --------------------------------------------
|
||||
# Production Stage
|
||||
# --------------------------------------------
|
||||
FROM node:19.2-alpine3.15 as production
|
||||
WORKDIR /app
|
||||
|
||||
# Copy the bundled code from the build stage to the production image
|
||||
COPY --chown=node:node --from=build /svr/app/node_modules ./node_modules
|
||||
COPY --chown=node:node --from=build /svr/app/dist ./dist
|
||||
COPY --chown=node:node --from=build /svr/app/package*.json ./
|
||||
COPY --chown=node:node --from=build /svr/app/prisma ./prisma
|
||||
COPY --from=build /app/dist/apps/backend ./backend
|
||||
COPY --from=build /app/dist/libs/prisma ./prisma
|
||||
COPY --from=build /app/node_modules ./node_modules
|
||||
COPY ./libs/ ./libs/
|
||||
COPY ./nx.json ./nx.json
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
# Start the server using the production build
|
||||
CMD [ "npm", "run", "start:migrate:prod" ]
|
||||
# Start the application
|
||||
CMD sh -c "npx nx migrate-dev prisma --name=init && node backend/main.js"
|
11
apps/backend/jest.config.ts
Normal file
11
apps/backend/jest.config.ts
Normal file
@ -0,0 +1,11 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'backend',
|
||||
preset: '../../jest.preset.js',
|
||||
testEnvironment: 'node',
|
||||
transform: {
|
||||
'^.+\\.[tj]s$': ['ts-jest', { tsconfig: '<rootDir>/tsconfig.spec.json' }],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageDirectory: '../../coverage/apps/backend',
|
||||
};
|
12647
apps/backend/package-lock.json
generated
12647
apps/backend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,90 +0,0 @@
|
||||
{
|
||||
"name": "reduced.to-backend",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"test": "jest --silent",
|
||||
"build": "nest build",
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"start:migrate:prod": "prisma migrate deploy && npm run start:prod",
|
||||
"fmt": "prettier --write .",
|
||||
"fmt.check": "prettier --check ."
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/origranot/url-shortener.git"
|
||||
},
|
||||
"author": "OriG",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/origranot/url-shortener/issues"
|
||||
},
|
||||
"homepage": "https://github.com/origranot/url-shortener#readme",
|
||||
"dependencies": {
|
||||
"@nestjs/cache-manager": "^2.0.0",
|
||||
"@nestjs/cli": "^10.0.3",
|
||||
"@nestjs/common": "^10.0.3",
|
||||
"@nestjs/config": "^3.0.0",
|
||||
"@nestjs/core": "^10.0.3",
|
||||
"@nestjs/jwt": "^10.1.0",
|
||||
"@nestjs/passport": "^10.0.0",
|
||||
"@nestjs/platform-express": "^10.0.3",
|
||||
"@nestjs/schematics": "^10.0.1",
|
||||
"@nestjs/testing": "^10.0.3",
|
||||
"@nestjs/throttler": "^4.1.0",
|
||||
"@novu/node": "^0.16.0",
|
||||
"@origranot/ts-logger": "^1.12.0",
|
||||
"@prisma/client": "^4.16.2",
|
||||
"bcrypt": "^5.1.0",
|
||||
"body-parser": "^1.20.2",
|
||||
"cache-manager": "^5.2.3",
|
||||
"cache-manager-redis-store": "^3.0.1",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"cookie-parser": "^1.4.6",
|
||||
"express": "^4.18.2",
|
||||
"jest": "^29.5.0",
|
||||
"passport": "^0.6.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"redis": "^4.6.7",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"ts-jest": "^29.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bcrypt": "^5.0.0",
|
||||
"@types/cache-manager": "^4.0.2",
|
||||
"@types/cookie-parser": "^1.4.3",
|
||||
"@types/express": "^4.17.17",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/node": "^20.3.1",
|
||||
"@types/passport-jwt": "^3.0.8",
|
||||
"@types/passport-local": "^1.0.35",
|
||||
"@types/supertest": "^2.0.12",
|
||||
"prettier": "2.8.8",
|
||||
"prisma": "^4.16.2",
|
||||
"source-map-support": "^0.5.21",
|
||||
"supertest": "^6.3.3",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typescript": "^5.1.3"
|
||||
},
|
||||
"jest": {
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
"json",
|
||||
"ts"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"testRegex": ".*\\.spec\\.ts$",
|
||||
"transform": {
|
||||
"^.+\\.(t|j)s$": "ts-jest"
|
||||
},
|
||||
"collectCoverageFrom": [
|
||||
"**/*.(t|j)s"
|
||||
],
|
||||
"coverageDirectory": "../coverage",
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "Role" AS ENUM ('USER', 'ADMIN');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"password" TEXT NOT NULL,
|
||||
"role" "Role" NOT NULL DEFAULT 'USER',
|
||||
"verified" BOOLEAN NOT NULL DEFAULT false,
|
||||
"verificationToken" TEXT,
|
||||
"refreshToken" TEXT,
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
90
apps/backend/project.json
Normal file
90
apps/backend/project.json
Normal file
@ -0,0 +1,90 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "apps/backend/src",
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nx/webpack:webpack",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"defaultConfiguration": "production",
|
||||
"options": {
|
||||
"target": "node",
|
||||
"compiler": "tsc",
|
||||
"outputPath": "dist/apps/backend",
|
||||
"main": "apps/backend/src/main.ts",
|
||||
"tsConfig": "apps/backend/tsconfig.app.json",
|
||||
"isolatedConfig": true,
|
||||
"webpackConfig": "apps/backend/webpack.config.js"
|
||||
},
|
||||
"dependsOn": [
|
||||
{
|
||||
"dependencies": true,
|
||||
"target": "build"
|
||||
}
|
||||
],
|
||||
"configurations": {
|
||||
"development": {},
|
||||
"production": {}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@nx/js:node",
|
||||
"defaultConfiguration": "development",
|
||||
"options": {
|
||||
"buildTarget": "backend:build"
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildTarget": "backend:build:development"
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "backend:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/backend/**/*.ts"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "apps/backend/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
},
|
||||
"configurations": {
|
||||
"ci": {
|
||||
"ci": true,
|
||||
"codeCoverage": true
|
||||
}
|
||||
},
|
||||
"dependsOn": [
|
||||
{
|
||||
"dependencies": true,
|
||||
"target": "build"
|
||||
}
|
||||
]
|
||||
},
|
||||
"docker-build": {
|
||||
"dependsOn": ["build"],
|
||||
"command": "docker build -f apps/backend/Dockerfile . -t backend"
|
||||
},
|
||||
"push-image-to-registry": {
|
||||
"dependsOn": ["docker-build"],
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"commands": [
|
||||
"docker image tag backend ghcr.io/{args.repository}/backend:{args.github-sha}",
|
||||
"docker push ghcr.io/{args.repository}/backend:{args.github-sha}"
|
||||
],
|
||||
"parallel": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
@ -7,7 +7,7 @@ import { AppConfigModule } from './config/config.module';
|
||||
import { AppConfigService } from './config/config.service';
|
||||
import { AppLoggerModule } from './logger/logger.module';
|
||||
import { NovuModule } from './novu/novu.module';
|
||||
import { PrismaService } from './prisma/prisma.service';
|
||||
import { PrismaService } from '@reduced.to/prisma';
|
||||
import { UniqueConstraint } from './shared/decorators/unique/unique.decorator';
|
||||
import { CustomThrottlerGuard } from './shared/guards/custom-throttler/custom-throttler';
|
||||
import { ShortenerModule } from './shortener/shortener.module';
|
||||
|
@ -4,7 +4,7 @@ import { agent as supertest, SuperAgentTest } from 'supertest';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { AuthService } from './auth.service';
|
||||
import { NovuService } from '../novu/novu.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { PrismaService } from '@reduced.to/prisma';
|
||||
import { AppConfigService } from '../config/config.service';
|
||||
import { SignupDto } from './dto/signup.dto';
|
||||
import { UserContext } from './interfaces/user-context';
|
||||
@ -14,7 +14,6 @@ import { JwtAuthGuard } from './guards/jwt.guard';
|
||||
import { VerifyAuthGuard } from './guards/verify.guard';
|
||||
import { UniqueConstraint } from '../shared/decorators/unique/unique.decorator';
|
||||
import { useContainer } from 'class-validator';
|
||||
import { AppModule } from '../app.module';
|
||||
|
||||
describe('AuthController', () => {
|
||||
let app: INestApplication;
|
||||
|
@ -2,7 +2,7 @@ import { Body, Controller, Get, Post, Req, Res, UnauthorizedException, UseGuards
|
||||
import { Request, Response } from 'express';
|
||||
import { AppConfigService } from '../config/config.service';
|
||||
import { NovuService } from '../novu/novu.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { PrismaService } from '@reduced.to/prisma';
|
||||
import { AuthService } from './auth.service';
|
||||
import { SignupDto } from './dto/signup.dto';
|
||||
import { JwtRefreshAuthGuard } from './guards/jwt-refresh.guard';
|
||||
|
@ -4,7 +4,7 @@ import { PassportModule } from '@nestjs/passport';
|
||||
import { AppConfigService } from '../config/config.service';
|
||||
import { NovuModule } from '../novu/novu.module';
|
||||
import { NovuService } from '../novu/novu.service';
|
||||
import { PrismaModule } from '../prisma/prisma.module';
|
||||
import { PrismaModule } from '@reduced.to/prisma';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { AuthService } from './auth.service';
|
||||
import { JwtRefreshStrategy } from './strategies/jwt-refresh.strategy';
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { UnauthorizedException } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { Role, User } from '@prisma/client';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import { Role, User } from '@reduced.to/prisma';
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
import { AppConfigModule } from '../config/config.module';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { PrismaService } from '@reduced.to/prisma';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
describe('AuthService', () => {
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { Role } from '@prisma/client';
|
||||
import * as bcrypt from 'bcrypt';
|
||||
import { Role } from '@reduced.to/prisma';
|
||||
import * as bcrypt from 'bcryptjs';
|
||||
import { AppConfigService } from '../config/config.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { PrismaService } from '@reduced.to/prisma';
|
||||
import { SignupDto } from './dto/signup.dto';
|
||||
import { UserContext } from './interfaces/user-context';
|
||||
|
||||
|
@ -2,7 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ExecutionContext, UnauthorizedException } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { RolesGuard } from './roles.guard';
|
||||
import { Role } from '@prisma/client';
|
||||
import { Role } from '@reduced.to/prisma';
|
||||
import { UserContext } from '../interfaces/user-context';
|
||||
|
||||
describe('Roles Guard', () => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { Role } from '@prisma/client';
|
||||
import { Role } from '@reduced.to/prisma';
|
||||
import { ROLES_KEY } from '../../shared/decorators/roles/roles.decorator';
|
||||
import { UserContext } from '../interfaces/user-context';
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Role } from '@prisma/client';
|
||||
import { Role } from '@reduced.to/prisma';
|
||||
|
||||
export interface UserContext {
|
||||
id: string;
|
||||
|
@ -2,7 +2,7 @@ import { ValidationPipe, VersioningType } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import { useContainer } from 'class-validator';
|
||||
import * as cookieParser from 'cookie-parser';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import { AppModule } from './app.module';
|
||||
import { AppConfigService } from './config/config.service';
|
||||
|
||||
|
@ -1,18 +0,0 @@
|
||||
import { PrismaService } from './prisma.service';
|
||||
import { Test } from '@nestjs/testing';
|
||||
|
||||
describe('PrismaService', () => {
|
||||
let service: PrismaService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const app = await Test.createTestingModule({
|
||||
providers: [PrismaService],
|
||||
}).compile();
|
||||
|
||||
service = app.get<PrismaService>(PrismaService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
@ -1,15 +0,0 @@
|
||||
import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
|
||||
@Injectable()
|
||||
export class PrismaService extends PrismaClient implements OnModuleInit {
|
||||
async onModuleInit() {
|
||||
await this.$connect();
|
||||
}
|
||||
|
||||
async enableShutdownHooks(app: INestApplication) {
|
||||
this.$on('beforeExit', async () => {
|
||||
await app.close();
|
||||
});
|
||||
}
|
||||
}
|
@ -17,7 +17,9 @@ describe('IsVerified Decorator', () => {
|
||||
// Sample controller to simulate the context
|
||||
class TestController {
|
||||
@IsVerified()
|
||||
testMethod() {}
|
||||
testMethod() {
|
||||
// Empty method
|
||||
}
|
||||
}
|
||||
|
||||
const testController = new TestController();
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { Role } from '@prisma/client';
|
||||
import { Role } from '@reduced.to/prisma';
|
||||
import { Roles, ROLES_KEY } from './roles.decorator';
|
||||
|
||||
describe('Roles Decorator', () => {
|
||||
@ -20,7 +20,9 @@ describe('Roles Decorator', () => {
|
||||
// Sample controller to simulate the context
|
||||
class TestController {
|
||||
@Roles(...roles)
|
||||
testMethod() {}
|
||||
testMethod() {
|
||||
// Empty method
|
||||
}
|
||||
}
|
||||
|
||||
const testController = new TestController();
|
||||
@ -35,7 +37,9 @@ describe('Roles Decorator', () => {
|
||||
// Sample controller to simulate the context
|
||||
class TestController {
|
||||
@Roles(...roles)
|
||||
testMethod() {}
|
||||
testMethod() {
|
||||
// Empty method
|
||||
}
|
||||
}
|
||||
|
||||
const testController = new TestController();
|
||||
@ -47,7 +51,9 @@ describe('Roles Decorator', () => {
|
||||
it('should not set any metadata of roles', () => {
|
||||
// Sample controller to simulate the context
|
||||
class TestController {
|
||||
testMethod() {}
|
||||
testMethod() {
|
||||
// Empty method
|
||||
}
|
||||
}
|
||||
|
||||
const testController = new TestController();
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
import { Role } from '@prisma/client';
|
||||
import { Role } from '@reduced.to/prisma';
|
||||
|
||||
export const ROLES_KEY = 'roles';
|
||||
export const Roles = (...roles: Role[]) => SetMetadata(ROLES_KEY, roles);
|
||||
|
@ -12,7 +12,7 @@ describe('Sortable Decorator', () => {
|
||||
it('should pass validation if sort field is empty', async () => {
|
||||
const dto = new TestDto();
|
||||
|
||||
let errors = await validate(dto);
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
|
||||
@ -25,7 +25,7 @@ describe('Sortable Decorator', () => {
|
||||
role: SortOrder.ASCENDING,
|
||||
};
|
||||
|
||||
let errors = await validate(dto);
|
||||
const errors = await validate(dto);
|
||||
expect(errors.length).toBe(0);
|
||||
});
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { UniqueConstraint, Unique } from './unique.decorator';
|
||||
import { PrismaService } from '../../../prisma/prisma.service';
|
||||
import { PrismaService } from '@reduced.to/prisma';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ValidationArguments } from 'class-validator';
|
||||
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
ValidationArguments,
|
||||
} from 'class-validator';
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../../../prisma/prisma.service';
|
||||
import { PrismaService } from '@reduced.to/prisma';
|
||||
|
||||
@ValidatorConstraint({ name: 'Unique', async: true })
|
||||
@Injectable()
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ShortenerController } from './shortener.controller';
|
||||
import { ShortenerService } from './shortener.service';
|
||||
import { PrismaModule } from '../prisma/prisma.module';
|
||||
import { PrismaModule } from '@reduced.to/prisma';
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
|
@ -4,7 +4,7 @@ import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AppCacheModule } from '../cache/cache.module';
|
||||
import { AppCacheService } from '../cache/cache.service';
|
||||
import { AppConfigModule } from '../config/config.module';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { PrismaService } from '@reduced.to/prisma';
|
||||
import { ShortenerDto } from './dto';
|
||||
import { BadRequestException } from '@nestjs/common';
|
||||
import { UserContext } from '../auth/interfaces/user-context';
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { AppCacheService } from '../cache/cache.service';
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
import { AppConfigService } from '../config/config.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { PrismaService } from '@reduced.to/prisma';
|
||||
import { ShortenerDto } from './dto';
|
||||
import { UserContext } from '../auth/interfaces/user-context';
|
||||
import { Url } from '@prisma/client';
|
||||
import { Url } from '@reduced.to/prisma';
|
||||
import { calculateDateFromTtl } from '../shared/utils';
|
||||
|
||||
@Injectable()
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication, ValidationPipe } from '@nestjs/common';
|
||||
import * as request from 'supertest';
|
||||
import request from 'supertest';
|
||||
import { UsersController } from './users.controller';
|
||||
import { IFindAllOptions, UsersService } from './users.service';
|
||||
import { User } from '@prisma/client';
|
||||
import { User } from '@reduced.to/prisma';
|
||||
import { JwtAuthGuard } from '../auth/guards/jwt.guard';
|
||||
import { AppConfigModule } from '../config/config.module';
|
||||
import { RolesGuard } from '../auth/guards/roles.guard';
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
|
||||
import { Role, User } from '@prisma/client';
|
||||
import { Role, User } from '@reduced.to/prisma';
|
||||
import { JwtAuthGuard } from '../auth/guards/jwt.guard';
|
||||
import { RolesGuard } from '../auth/guards/roles.guard';
|
||||
import { Roles } from '../shared/decorators/roles/roles.decorator';
|
||||
|
@ -1,9 +1,8 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AuthModule } from '../auth/auth.module';
|
||||
import { RolesGuard } from '../auth/guards/roles.guard';
|
||||
import { PrismaModule } from '../prisma/prisma.module';
|
||||
import { UsersController } from './users.controller';
|
||||
import { UsersService } from './users.service';
|
||||
import { PrismaModule } from '@reduced.to/prisma';
|
||||
|
||||
@Module({
|
||||
imports: [AuthModule, PrismaModule],
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { IFindAllOptions, UsersService } from './users.service';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { PrismaService } from '@reduced.to/prisma';
|
||||
import { SortOrder } from '../shared/enums/sort-order.enum';
|
||||
|
||||
describe('UsersService', () => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { User } from '@prisma/client';
|
||||
import { PrismaService } from '@reduced.to/prisma';
|
||||
import { User } from '@reduced.to/prisma';
|
||||
import { IPaginationResult, orderByBuilder } from '../shared/utils';
|
||||
import { SortOrder } from '../shared/enums/sort-order.enum';
|
||||
|
||||
|
12
apps/backend/tsconfig.app.json
Normal file
12
apps/backend/tsconfig.app.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["node"],
|
||||
"emitDecoratorMetadata": true,
|
||||
"target": "es2021"
|
||||
},
|
||||
"exclude": ["jest.config.ts", "src/**/*.spec.ts", "src/**/*.test.ts"],
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
|
||||
}
|
@ -1,21 +1,16 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": true,
|
||||
"removeComments": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"target": "es2017",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": false,
|
||||
"noImplicitAny": false,
|
||||
"strictBindCallApply": false,
|
||||
"forceConsistentCasingInFileNames": false,
|
||||
"noFallthroughCasesInSwitch": false
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
|
14
apps/backend/tsconfig.spec.json
Normal file
14
apps/backend/tsconfig.spec.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
8
apps/backend/webpack.config.js
Normal file
8
apps/backend/webpack.config.js
Normal file
@ -0,0 +1,8 @@
|
||||
const { composePlugins, withNx } = require('@nx/webpack');
|
||||
|
||||
// Nx plugins for webpack.
|
||||
module.exports = composePlugins(withNx(), (config) => {
|
||||
// Update the webpack config as needed here.
|
||||
// e.g. `config.plugins.push(new MyPlugin())`
|
||||
return config;
|
||||
});
|
@ -1,36 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
node: true,
|
||||
},
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:qwik/recommended'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
tsconfigRootDir: __dirname,
|
||||
project: ['./tsconfig.json'],
|
||||
ecmaVersion: 2021,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true,
|
||||
},
|
||||
},
|
||||
plugins: ['@typescript-eslint'],
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-inferrable-types': 'off',
|
||||
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||
'@typescript-eslint/no-empty-interface': 'off',
|
||||
'@typescript-eslint/no-namespace': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/no-this-alias': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'prefer-spread': 'off',
|
||||
'no-case-declarations': 'off',
|
||||
'no-console': 'off',
|
||||
'@typescript-eslint/no-unused-vars': ['error'],
|
||||
},
|
||||
};
|
28
apps/frontend/.eslintrc.json
Normal file
28
apps/frontend/.eslintrc.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "../../.eslintrc.json"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": ["apps/frontend/tsconfig.*?.json"],
|
||||
"ecmaVersion": 2021,
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"jsx": true
|
||||
}
|
||||
},
|
||||
"plugins": ["@typescript-eslint"],
|
||||
"ignorePatterns": ["!**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts", "*.tsx", "*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.ts", "*.tsx"],
|
||||
"rules": {}
|
||||
},
|
||||
{
|
||||
"files": ["*.js", "*.jsx"],
|
||||
"rules": {}
|
||||
}
|
||||
]
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
DOMAIN=localhost
|
||||
API_DOMAIN=http://localhost:3000
|
||||
API_DOMAIN=http://backend:3000 # Use this for internal (Server sided) API calls
|
45
apps/frontend/.gitignore
vendored
45
apps/frontend/.gitignore
vendored
@ -1,45 +0,0 @@
|
||||
# Build
|
||||
/dist
|
||||
/lib
|
||||
/lib-types
|
||||
/server
|
||||
|
||||
# Temp
|
||||
/temp
|
||||
/tmp
|
||||
|
||||
# Development
|
||||
node_modules
|
||||
|
||||
# Cache
|
||||
.cache
|
||||
.mf
|
||||
.vscode
|
||||
.rollup.cache
|
||||
tsconfig.tsbuildinfo
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Editor
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# Yarn
|
||||
.yarn/*
|
||||
!.yarn/releases
|
||||
|
||||
# env
|
||||
.env
|
@ -1,36 +1,51 @@
|
||||
ARG API_DOMAIN
|
||||
ARG CLIENTSIDE_API_DOMAIN
|
||||
ARG DOMAIN
|
||||
|
||||
# --------------------------------------------
|
||||
# Dependencies Stage
|
||||
# --------------------------------------------
|
||||
FROM node:19.2-alpine3.15 as dependencies
|
||||
|
||||
WORKDIR /app
|
||||
COPY package.json package-lock.json ./
|
||||
|
||||
RUN apk add --update python3 make g++\
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
RUN npm ci
|
||||
|
||||
|
||||
# --------------------------------------------
|
||||
# Build Stage
|
||||
# --------------------------------------------
|
||||
# Intermediate docker image to build the bundle in and install dependencies
|
||||
FROM node:19.2-alpine3.15 as build
|
||||
WORKDIR /app
|
||||
|
||||
# Set the working directory to /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
COPY . .
|
||||
COPY --from=dependencies /app/node_modules ./node_modules
|
||||
|
||||
# Copy the package.json and package-lock.json over in the intermedate "build" image
|
||||
COPY ./package.json ./
|
||||
COPY ./package-lock.json ./
|
||||
# Run prisma generate & build the bundle in production mode
|
||||
RUN npx nx build frontend --prod --skip-nx-cache
|
||||
|
||||
# Clean install because we want to install the exact versions (Include dev dependencies)
|
||||
RUN npm ci --include=dev
|
||||
|
||||
# Copy the source code into the build image
|
||||
COPY ./ ./
|
||||
|
||||
# Build the project
|
||||
RUN npm run build
|
||||
|
||||
# Pull the same Node image and use it as the final (production image)
|
||||
# --------------------------------------------
|
||||
# Production Stage
|
||||
# --------------------------------------------
|
||||
FROM node:19.2-alpine3.15 as production
|
||||
WORKDIR /app
|
||||
|
||||
# Set the working directory to /usr/src/app
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Only copy the results from the build over to the final image
|
||||
# We do this to keep the final image as small as possible
|
||||
COPY --from=build /usr/src/app/node_modules ./node_modules
|
||||
COPY --from=build /usr/src/app/server ./server
|
||||
COPY --from=build /usr/src/app/dist ./dist
|
||||
COPY --from=build /app/dist/apps/frontend ./frontend
|
||||
COPY --from=build /app/node_modules ./node_modules
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
# Start the application
|
||||
CMD [ "node", "server/entry.express"]
|
||||
CMD [ "node", "frontend/server/entry.express.js"]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,41 +0,0 @@
|
||||
FROM node:18-alpine3.15 AS build
|
||||
|
||||
# Set NODE_ENV environment variable
|
||||
ENV NODE_ENV production
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /app
|
||||
|
||||
# Install app dependencies
|
||||
COPY *.json ./
|
||||
|
||||
# Copy all files to build stage
|
||||
COPY . .
|
||||
|
||||
# Define Buildtime vairables from workflow
|
||||
ARG API_DOMAIN=${API_DOMAIN}
|
||||
ARG DOMAIN=${DOMAIN}
|
||||
|
||||
# Install dependencies for production
|
||||
RUN npm ci --include=dev
|
||||
|
||||
# Building code for production
|
||||
RUN npm run build
|
||||
|
||||
# Build static files
|
||||
RUN npm run build.server
|
||||
|
||||
# Final stage
|
||||
FROM node:18-alpine3.15 as production
|
||||
|
||||
# Create app directory
|
||||
WORKDIR /app
|
||||
|
||||
# Copy build
|
||||
COPY --from=build /app/node_modules/ ./node_modules/
|
||||
COPY --from=build /app/dist/ ./dist/
|
||||
COPY --from=build /app/server/ ./server/
|
||||
|
||||
EXPOSE 5000
|
||||
|
||||
CMD ["sh", "-c", "ORIGIN=https://reduced.to node server/entry.express.js" ]
|
@ -1,18 +1,17 @@
|
||||
# Qwik App ⚡️
|
||||
# Qwik City App ⚡️
|
||||
|
||||
- [Qwik Docs](https://qwik.builder.io/)
|
||||
- [Discord](https://qwik.builder.io/chat)
|
||||
- [Qwik GitHub](https://github.com/BuilderIO/qwik)
|
||||
- [@QwikDev](https://twitter.com/QwikDev)
|
||||
- [Vite](https://vitejs.dev/)
|
||||
- [Partytown](https://partytown.builder.io/)
|
||||
- [Mitosis](https://github.com/BuilderIO/mitosis)
|
||||
- [Builder.io](https://www.builder.io/)
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
This project is using Qwik with [QwikCity](https://qwik.builder.io/qwikcity/overview/). QwikCity is just a extra set of tools on top of Qwik to make it easier to build a full site, including directory-based routing, layouts, and more.
|
||||
|
||||
Inside your project, you'll see the following directory structure:
|
||||
|
||||
```
|
||||
@ -31,12 +30,12 @@ Inside your project, you'll see the following directory structure:
|
||||
|
||||
- `public`: Any static assets, like images, can be placed in the public directory. Please see the [Vite public directory](https://vitejs.dev/guide/assets.html#the-public-directory) for more info.
|
||||
|
||||
## Add Integrations
|
||||
## Add Integrations and deployment
|
||||
|
||||
Use the `npm run qwik add` command to add additional integrations. Some examples of integrations include: Cloudflare, Netlify or Vercel server, and the [Static Site Generator (SSG)](https://qwik.builder.io/qwikcity/static-site-generation/static-site-config/).
|
||||
Use the `pnpm qwik add` command to add additional integrations. Some examples of integrations include: Cloudflare, Netlify or Express server, and the [Static Site Generator (SSG)](https://qwik.builder.io/qwikcity/static-site-generation/static-site-config/).
|
||||
|
||||
```shell
|
||||
npm run qwik add # or `yarn qwik add`
|
||||
pnpm qwik add # or `yarn qwik add`
|
||||
```
|
||||
|
||||
## Development
|
||||
@ -44,7 +43,7 @@ npm run qwik add # or `yarn qwik add`
|
||||
Development mode uses [Vite's development server](https://vitejs.dev/). During development, the `dev` command will server-side render (SSR) the output.
|
||||
|
||||
```shell
|
||||
npm run dev # or `yarn dev`
|
||||
npm start # or `yarn start`
|
||||
```
|
||||
|
||||
> Note: during dev mode, Vite may request a significant number of `.js` files. This does not represent a Qwik production build.
|
||||
@ -54,7 +53,7 @@ npm run dev # or `yarn dev`
|
||||
The preview command will create a production build of the client modules, a production build of `src/entry.preview.tsx`, and run a local server. The preview server is only for convenience to locally preview a production build, and it should not be used as a production server.
|
||||
|
||||
```shell
|
||||
npm run preview # or `yarn preview`
|
||||
pnpm preview # or `yarn preview`
|
||||
```
|
||||
|
||||
## Production
|
||||
@ -62,27 +61,5 @@ npm run preview # or `yarn preview`
|
||||
The production build will generate client and server modules by running both client and server build commands. Additionally, the build command will use Typescript to run a type check on the source code.
|
||||
|
||||
```shell
|
||||
npm run build # or `yarn build`
|
||||
pnpm build # or `yarn build`
|
||||
```
|
||||
|
||||
## Static Site Generator (Node.js)
|
||||
|
||||
```
|
||||
npm run ssg
|
||||
```
|
||||
|
||||
## Static Site Generator (Node.js)
|
||||
|
||||
```
|
||||
npm run ssg
|
||||
```
|
||||
|
||||
## Express Server
|
||||
|
||||
This app has a minimal [Express server](https://expressjs.com/) implementation. After running a full build, you can preview the build using the command:
|
||||
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
Then visit [http://localhost:8080/](http://localhost:8080/)
|
||||
|
@ -7,7 +7,7 @@ export default extendConfig(baseConfig, () => {
|
||||
build: {
|
||||
ssr: true,
|
||||
rollupOptions: {
|
||||
input: ['src/entry.express.tsx', '@qwik-city-plan'],
|
||||
input: ['apps/frontend/src/entry.express.tsx', '@qwik-city-plan'],
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
|
6581
apps/frontend/package-lock.json
generated
6581
apps/frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,55 +0,0 @@
|
||||
{
|
||||
"name": "reduced.to-frontend",
|
||||
"description": "",
|
||||
"engines": {
|
||||
"node": ">=15.0.0"
|
||||
},
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "qwik build",
|
||||
"build.client": "vite build",
|
||||
"build.preview": "vite build --ssr src/entry.preview.tsx",
|
||||
"build.server": "vite build -c adapters/express/vite.config.ts",
|
||||
"build.types": "tsc --incremental --noEmit",
|
||||
"dev": "vite --mode ssr",
|
||||
"dev.debug": "node --inspect-brk ./node_modules/vite/bin/vite.js --mode ssr --force",
|
||||
"fmt": "prettier --write .",
|
||||
"fmt.check": "prettier --check .",
|
||||
"lint": "eslint \"src/**/*.ts*\"",
|
||||
"preview": "qwik build preview && vite preview --open",
|
||||
"serve": "node server/entry.express",
|
||||
"start": "vite --open --mode ssr",
|
||||
"qwik": "qwik"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@builder.io/qwik": "^1.2.3",
|
||||
"@builder.io/qwik-city": "^1.2.3",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/canvas-confetti": "^1.4.3",
|
||||
"@types/eslint": "8.4.6",
|
||||
"@types/express": "4.17.13",
|
||||
"@types/node": "latest",
|
||||
"@types/qrcode": "^1.5.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.37.0",
|
||||
"@typescript-eslint/parser": "5.37.0",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"daisyui": "^2.51.5",
|
||||
"dotenv": "^16.0.3",
|
||||
"eslint": "8.23.1",
|
||||
"eslint-plugin-qwik": "^1.2.3",
|
||||
"express": "^4.18.2",
|
||||
"node-fetch": "^3.2.10",
|
||||
"nodemon": "^2.0.20",
|
||||
"postcss": "^8.4.17",
|
||||
"tailwindcss": "^3.3.2",
|
||||
"typescript": "4.8.3",
|
||||
"vite": "^4.3.9",
|
||||
"vite-tsconfig-paths": "3.5.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.1.2",
|
||||
"canvas-confetti": "^1.5.1",
|
||||
"jwt-decode": "^3.1.2",
|
||||
"qrcode": "^1.5.1"
|
||||
}
|
||||
}
|
@ -1,6 +1,11 @@
|
||||
const { join } = require('path');
|
||||
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
'tailwindcss/nesting': {},
|
||||
autoprefixer: {},
|
||||
tailwindcss: {
|
||||
config: join(__dirname, 'tailwind.config.js'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
86
apps/frontend/project.json
Normal file
86
apps/frontend/project.json
Normal file
@ -0,0 +1,86 @@
|
||||
{
|
||||
"name": "frontend",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"sourceRoot": "apps/frontend/src",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "qwik-nx:build",
|
||||
"options": {
|
||||
"runSequence": ["frontend:build.client", "frontend:build.ssr"],
|
||||
"outputPath": "dist/apps/frontend"
|
||||
},
|
||||
"configurations": {
|
||||
"preview": {}
|
||||
}
|
||||
},
|
||||
"build.client": {
|
||||
"executor": "@nx/vite:build",
|
||||
"options": {
|
||||
"outputPath": "dist/apps/frontend",
|
||||
"configFile": "apps/frontend/vite.config.ts"
|
||||
}
|
||||
},
|
||||
"build.ssr": {
|
||||
"executor": "@nx/vite:build",
|
||||
"defaultConfiguration": "express",
|
||||
"options": {
|
||||
"outputPath": "dist/apps/frontend",
|
||||
"configFile": "apps/frontend/adapters/express/vite.config.ts"
|
||||
}
|
||||
},
|
||||
"preview": {
|
||||
"executor": "@nx/vite:preview-server",
|
||||
"options": {
|
||||
"buildTarget": "frontend:build",
|
||||
"port": 4300
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nx/vite:test",
|
||||
"outputs": ["../../coverage/apps/frontend"],
|
||||
"options": {
|
||||
"passWithNoTests": true,
|
||||
"reportsDirectory": "../../coverage/apps/frontend"
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@nx/vite:dev-server",
|
||||
"options": {
|
||||
"buildTarget": "frontend:build.client",
|
||||
"mode": "ssr",
|
||||
"port": 4200
|
||||
}
|
||||
},
|
||||
"serve.debug": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"command": "node --inspect-brk ../../node_modules/vite/bin/vite.js --mode ssr --force",
|
||||
"cwd": "apps/frontend"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/frontend/**/*.{ts,tsx,js,jsx}"]
|
||||
}
|
||||
},
|
||||
"docker-build": {
|
||||
"dependsOn": ["build"],
|
||||
"command": "docker build -f apps/frontend/Dockerfile . -t frontend"
|
||||
},
|
||||
"push-image-to-registry": {
|
||||
"dependsOn": ["docker-build"],
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"commands": [
|
||||
"docker image tag frontend ghcr.io/{args.repository}/frontend:{args.github-sha}",
|
||||
"docker push ghcr.io/{args.repository}/frontend:{args.github-sha}"
|
||||
],
|
||||
"parallel": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
@ -1,13 +1,13 @@
|
||||
import { confettiAnimate } from '~/lib/confetti';
|
||||
import { Store } from '~/routes';
|
||||
import { copyToClipboard, normalizeUrl } from '~/utils';
|
||||
import { confettiAnimate } from '../../lib/confetti';
|
||||
import { Store } from '../../routes';
|
||||
import { copyToClipboard, normalizeUrl } from '../../utils';
|
||||
|
||||
/**
|
||||
* Returns the shorter link from the server.
|
||||
* @param {string} originalUrl - The original url we want to shorten.
|
||||
*/
|
||||
const getShortenUrl = async (originalUrl: string, ttl: number) => {
|
||||
const result = await fetch(`${process.env.API_DOMAIN}/api/v1/shortener`, {
|
||||
const result = await fetch(`${process.env.CLIENTSIDE_API_DOMAIN}/api/v1/shortener`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { component$, useContext, $, useOnDocument, useSignal, QwikKeyboardEvent } from '@builder.io/qwik';
|
||||
import { InputContext } from '~/routes';
|
||||
import { InputContext } from '../../routes';
|
||||
import { ShortenerInputBtn } from './shortener-input-btn';
|
||||
import { TIME_FRAME_DIR } from './constants';
|
||||
import { useGetCurrentUser } from '~/routes/layout';
|
||||
import { Select } from '~/components/select/select';
|
||||
import { ArrowDoodle } from '~/components/arrow-doodle/arrow-doodle';
|
||||
import { useGetCurrentUser } from '../../routes/layout';
|
||||
import { Select } from '../../components/select/select';
|
||||
import { ArrowDoodle } from '../../components/arrow-doodle/arrow-doodle';
|
||||
|
||||
export interface ShortenerInputProps {
|
||||
onKeyUp$: (event: QwikKeyboardEvent<HTMLInputElement>) => void;
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { $, component$, useContext, useStylesScoped$ } from '@builder.io/qwik';
|
||||
import { GlobalStore } from '~/context';
|
||||
import { GlobalStore } from '../../context';
|
||||
import { themeStorageKey } from '../router-head/theme-script';
|
||||
import styles from './theme-switcher.css?inline';
|
||||
|
||||
@ -7,13 +7,20 @@ export const DARK_THEME = 'dracula';
|
||||
export const LIGHT_THEME = 'light';
|
||||
export type ThemePreference = typeof DARK_THEME | typeof LIGHT_THEME;
|
||||
|
||||
export const colorSchemeChangeListener = (onColorSchemeChange: (isDark: boolean) => void) => {
|
||||
export const colorSchemeChangeListener = (
|
||||
onColorSchemeChange: (isDark: boolean) => void
|
||||
) => {
|
||||
const listener = ({ matches: isDark }: MediaQueryListEvent) => {
|
||||
onColorSchemeChange(isDark);
|
||||
};
|
||||
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (event) => listener(event));
|
||||
window
|
||||
.matchMedia('(prefers-color-scheme: dark)')
|
||||
.addEventListener('change', (event) => listener(event));
|
||||
|
||||
return () => window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', listener);
|
||||
return () =>
|
||||
window
|
||||
.matchMedia('(prefers-color-scheme: dark)')
|
||||
.removeEventListener('change', listener);
|
||||
};
|
||||
|
||||
export const setPreference = (theme: ThemePreference) => {
|
||||
@ -30,7 +37,9 @@ export const getColorPreference = (): ThemePreference => {
|
||||
if (localStorage.getItem(themeStorageKey)) {
|
||||
return localStorage.getItem(themeStorageKey) as ThemePreference;
|
||||
} else {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? DARK_THEME : LIGHT_THEME;
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
||||
? DARK_THEME
|
||||
: LIGHT_THEME;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -15,15 +15,15 @@ import express from 'express';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { join } from 'node:path';
|
||||
import { IncomingMessage } from 'node:http';
|
||||
import { Http2ServerRequest } from 'node:http2';
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line
|
||||
interface QwikCityPlatform extends PlatformNode {}
|
||||
}
|
||||
|
||||
// import compression from 'compression';
|
||||
|
||||
// Directories where the static assets are located
|
||||
const distDir = join(fileURLToPath(import.meta.url), '..', '..', 'dist');
|
||||
const distDir = join(fileURLToPath(import.meta.url), '..', '..', 'client');
|
||||
const buildDir = join(distDir, 'build');
|
||||
|
||||
// Allow for dynamic port
|
||||
@ -34,7 +34,7 @@ const { router, notFound } = createQwikCity({
|
||||
render,
|
||||
qwikCityPlan,
|
||||
manifest,
|
||||
getClientConn: (request: IncomingMessage) => {
|
||||
getClientConn: (request: IncomingMessage | Http2ServerRequest) => {
|
||||
// We need to override the default getClientConn function to get the client IP address from the request headers (x-forwarded-for or x-real-ip)
|
||||
return {
|
||||
ip: (request.headers['x-forwarded-for'] as string) || (request.headers['x-real-ip'] as string) || undefined,
|
||||
|
@ -1,6 +1,18 @@
|
||||
/*
|
||||
* WHAT IS THIS FILE?
|
||||
*
|
||||
* It's the bundle entry point for `npm run preview`.
|
||||
* That is, serving your app built in production mode.
|
||||
*
|
||||
* Feel free to modify this file, but don't remove it!
|
||||
*
|
||||
* Learn more about Vite's preview command:
|
||||
* - https://vitejs.dev/config/preview-options.html#preview-options
|
||||
*
|
||||
*/
|
||||
import { createQwikCity } from '@builder.io/qwik-city/middleware/node';
|
||||
import qwikCityPlan from '@qwik-city-plan';
|
||||
import render from './entry.ssr';
|
||||
import qwikCityPlan from '@qwik-city-plan';
|
||||
|
||||
/**
|
||||
* The default export is the QwikCity adaptor used by Vite preview.
|
||||
|
@ -1,18 +1,18 @@
|
||||
import { component$, createContextId, useContextProvider, useSignal, useStore, useStylesScoped$ } from '@builder.io/qwik';
|
||||
import { DocumentHead } from '@builder.io/qwik-city';
|
||||
import animations from '~/assets/css/animations.css?inline';
|
||||
import { handleShareOnTwitter } from '~/components/home-buttons/actions';
|
||||
import { TwitterButton } from '~/components/home-buttons/twitter-button/twitter-button';
|
||||
import { Loader } from '~/components/loader/loader';
|
||||
import { generateQRCode } from '~/components/qr-code/handleQRCode';
|
||||
import { QRCode } from '~/components/qr-code/qr-code';
|
||||
import { handleShortener } from '~/components/shortener-input/handleShortener';
|
||||
import { ShortenerInput } from '~/components/shortener-input/shortener-input';
|
||||
import { Tooltip } from '~/components/tooltip/tooltip';
|
||||
import { Waves } from '~/components/waves/waves';
|
||||
import { copyToClipboard, openUrl } from '~/utils';
|
||||
import animations from '../assets/css/animations.css?inline';
|
||||
import { handleShareOnTwitter } from '../components/home-buttons/actions';
|
||||
import { TwitterButton } from '../components/home-buttons/twitter-button/twitter-button';
|
||||
import { Loader } from '../components/loader/loader';
|
||||
import { generateQRCode } from '../components/qr-code/handleQRCode';
|
||||
import { QRCode } from '../components/qr-code/qr-code';
|
||||
import { handleShortener } from '../components/shortener-input/handleShortener';
|
||||
import { ShortenerInput } from '../components/shortener-input/shortener-input';
|
||||
import { Tooltip } from '../components/tooltip/tooltip';
|
||||
import { Waves } from '../components/waves/waves';
|
||||
import { copyToClipboard, openUrl } from '../utils';
|
||||
import styles from './index.css?inline';
|
||||
import { TIME_FRAME_DIR } from '~/components/shortener-input/constants';
|
||||
import { TIME_FRAME_DIR } from '../components/shortener-input/constants';
|
||||
|
||||
export const InputContext = createContextId<Store>('input');
|
||||
|
||||
|
@ -13,7 +13,7 @@ export default component$(() => {
|
||||
useVisibleTask$(() => {
|
||||
store.token = params.token;
|
||||
|
||||
fetch(`${process.env.API_DOMAIN}/api/v1/auth/verify?token=${store.token}`, {
|
||||
fetch(`${process.env.CLIENTSIDE_API_DOMAIN}/api/v1/auth/verify?token=${store.token}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
token: store.token,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { component$, useStore, useVisibleTask$ } from '@builder.io/qwik';
|
||||
import { Link, RequestHandler } from '@builder.io/qwik-city';
|
||||
import { Loader } from '~/components/loader/loader';
|
||||
import { authorizedFetch, validateAccessToken } from '~/shared/auth.service';
|
||||
import { Loader } from '../../../components/loader/loader';
|
||||
import { authorizedFetch, validateAccessToken } from '../../../shared/auth.service';
|
||||
|
||||
export interface Store {
|
||||
isVerified: boolean;
|
||||
@ -24,7 +24,7 @@ export default component$(() => {
|
||||
});
|
||||
|
||||
useVisibleTask$(() => {
|
||||
authorizedFetch(`${process.env.API_DOMAIN}/api/v1/auth/verified`).then(async (response) => {
|
||||
authorizedFetch(`${process.env.CLIENTSIDE_API_DOMAIN}/api/v1/auth/verified`).then(async (response) => {
|
||||
const { verified } = await response.json();
|
||||
store.isVerified = verified;
|
||||
store.loading = false;
|
||||
@ -54,7 +54,7 @@ export default component$(() => {
|
||||
<button
|
||||
class="btn btn-primary"
|
||||
onClick$={async () => {
|
||||
authorizedFetch(`${process.env.API_DOMAIN}/api/v1/auth/resend`).then(() => {
|
||||
authorizedFetch(`${process.env.CLIENTSIDE_API_DOMAIN}/api/v1/auth/resend`).then(() => {
|
||||
store.resent = true;
|
||||
});
|
||||
}}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { component$, Slot } from '@builder.io/qwik';
|
||||
import NotFoundLayout from '~/layouts/NotFoundLayout';
|
||||
import NotFoundLayout from '../../layouts/NotFoundLayout';
|
||||
|
||||
export default component$(() => {
|
||||
return (
|
||||
|
@ -75,7 +75,7 @@ export const authorizedFetch = async (url: string, options = {}) => {
|
||||
};
|
||||
|
||||
export const refreshTokens = async (refreshToken: string): Promise<{ accessToken: string; refreshToken: string }> => {
|
||||
const res = await fetch(`${process.env.API_DOMAIN}/api/v1/auth/refresh`, {
|
||||
const res = await fetch(`${process.env.CLIENTSIDE_API_DOMAIN}/api/v1/auth/refresh`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
|
@ -2,12 +2,12 @@ export const copyToClipboard = async (data: string): Promise<void> => {
|
||||
return navigator.clipboard.writeText(data);
|
||||
};
|
||||
|
||||
export const openUrl = (url: string | URL, target: string = '_blank'): void => {
|
||||
export const openUrl = (url: string | URL, target = '_blank'): void => {
|
||||
window.open(url, target);
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize input url
|
||||
* Normalize url input
|
||||
* - add protocol 'http' if missing.
|
||||
* - correct protocol http/https if mistyped one character.
|
||||
* @param {string} url
|
||||
|
@ -1,6 +1,8 @@
|
||||
const { join } = require('path');
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ['./src/**/*.{js,ts,jsx,tsx}'],
|
||||
content: [join(__dirname, 'src/**/*.{js,ts,jsx,tsx,mdx}')],
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
|
9
apps/frontend/tsconfig.app.json
Normal file
9
apps/frontend/tsconfig.app.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc"
|
||||
},
|
||||
"files": [],
|
||||
"exclude": [],
|
||||
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx", ".eslintrc.json"]
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"target": "ES2017",
|
||||
@ -7,7 +8,6 @@
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "@builder.io/qwik",
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"moduleResolution": "node",
|
||||
"esModuleInterop": true,
|
||||
@ -16,11 +16,15 @@
|
||||
"isolatedModules": true,
|
||||
"outDir": "tmp",
|
||||
"noEmit": true,
|
||||
"types": ["node", "vite/client"],
|
||||
"paths": {
|
||||
"~/*": ["./src/*"]
|
||||
}
|
||||
"types": ["node", "vite/client", "vitest"]
|
||||
},
|
||||
"files": ["./.eslintrc.cjs"],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"include": ["src"]
|
||||
}
|
||||
|
19
apps/frontend/tsconfig.spec.json
Normal file
19
apps/frontend/tsconfig.spec.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../dist/out-tsc",
|
||||
"types": ["vitest/globals", "node"]
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
@ -1,20 +1,60 @@
|
||||
import { qwikCity } from '@builder.io/qwik-city/vite';
|
||||
import { qwikVite } from '@builder.io/qwik/optimizer';
|
||||
import { qwikCity } from '@builder.io/qwik-city/vite';
|
||||
import { defineConfig, loadEnv } from 'vite';
|
||||
import tsconfigPaths from 'vite-tsconfig-paths';
|
||||
import { qwikNxVite } from 'qwik-nx/plugins';
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
const env = loadEnv(mode, process.cwd(), '');
|
||||
|
||||
console.log('env', {
|
||||
DOMAIN: env.DOMAIN,
|
||||
CLIENTSIDE_API_DOMAIN: env.CLIENTSIDE_API_DOMAIN,
|
||||
API_DOMAIN: env.API_DOMAIN,
|
||||
});
|
||||
|
||||
return {
|
||||
plugins: [qwikCity(), qwikVite(), tsconfigPaths()],
|
||||
cacheDir: '../../node_modules/.vite/apps/frontend',
|
||||
plugins: [
|
||||
qwikNxVite(),
|
||||
qwikCity(),
|
||||
qwikVite({
|
||||
client: {
|
||||
outDir: '../../dist/apps/frontend/client',
|
||||
},
|
||||
ssr: {
|
||||
outDir: '../../dist/apps/frontend/server',
|
||||
},
|
||||
}),
|
||||
tsconfigPaths({ root: '../../' }),
|
||||
],
|
||||
define: {
|
||||
'process.env.API_DOMAIN': JSON.stringify(env.API_DOMAIN),
|
||||
'process.env.DOMAIN': JSON.stringify(env.DOMAIN),
|
||||
'process.env.API_DOMAIN': JSON.stringify(env.API_DOMAIN),
|
||||
'process.env.CLIENTSIDE_API_DOMAIN': JSON.stringify(env.CLIENTSIDE_API_DOMAIN),
|
||||
'process.env.NODE_ENV': JSON.stringify(mode),
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ['canvas-confetti'],
|
||||
},
|
||||
server: {
|
||||
fs: {
|
||||
// Allow serving files from the project root
|
||||
allow: ['../../'],
|
||||
},
|
||||
},
|
||||
preview: {
|
||||
headers: {
|
||||
'Cache-Control': 'public, max-age=600',
|
||||
},
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
cache: {
|
||||
dir: '../../node_modules/.vitest',
|
||||
},
|
||||
environment: 'node',
|
||||
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
@ -10,6 +10,8 @@ services:
|
||||
- POSTGRES_USER=postgres
|
||||
- POSTGRES_PASSWORD=postgres
|
||||
- POSTGRES_DB=reduced_to_db
|
||||
volumes:
|
||||
- ../../tmp/data/postgresql:/var/lib/postgresql/data
|
||||
ports:
|
||||
- '5432:5432'
|
||||
|
||||
@ -27,25 +29,26 @@ services:
|
||||
frontend:
|
||||
container_name: frontend
|
||||
build:
|
||||
context: ../../apps/frontend
|
||||
dockerfile: Dockerfile
|
||||
context: ../..
|
||||
dockerfile: apps/frontend/Dockerfile
|
||||
args:
|
||||
- DOMAIN=${DOMAIN}
|
||||
- API_DOMAIN=${API_DOMAIN}
|
||||
- CLIENTSIDE_API_DOMAIN=${CLIENTSIDE_API_DOMAIN}
|
||||
restart: always
|
||||
ports:
|
||||
- '5000:5000'
|
||||
env_file: ../../apps/frontend/.env
|
||||
volumes:
|
||||
- /app
|
||||
|
||||
backend:
|
||||
container_name: backend
|
||||
platform: linux/amd64
|
||||
build:
|
||||
context: ../../apps/backend
|
||||
dockerfile: Dockerfile
|
||||
context: ../..
|
||||
dockerfile: apps/backend/Dockerfile
|
||||
restart: always
|
||||
volumes:
|
||||
- ../../apps/backend:/svr/app/
|
||||
- /svr/app/node_modules
|
||||
- ../../apps/backend:/app/apps/backend/
|
||||
ports:
|
||||
- '3000:3000'
|
||||
env_file: ../../apps/backend/.env
|
||||
|
20
docs/.gitignore
vendored
20
docs/.gitignore
vendored
@ -1,20 +0,0 @@
|
||||
# Dependencies
|
||||
/node_modules
|
||||
|
||||
# Production
|
||||
/build
|
||||
|
||||
# Generated files
|
||||
.docusaurus
|
||||
.cache-loader
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
@ -1,8 +0,0 @@
|
||||
{
|
||||
"trailingComma": "es5",
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"useTabs": false,
|
||||
"printWidth": 130
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
# Website
|
||||
|
||||
This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
|
||||
|
||||
### Installation
|
||||
|
||||
```
|
||||
$ yarn
|
||||
```
|
||||
|
||||
### Local Development
|
||||
|
||||
```
|
||||
$ yarn start
|
||||
```
|
||||
|
||||
This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
|
||||
|
||||
### Build
|
||||
|
||||
```
|
||||
$ yarn build
|
||||
```
|
||||
|
||||
This command generates static content into the `build` directory and can be served using any static contents hosting service.
|
||||
|
||||
### Deployment
|
||||
|
||||
Using SSH:
|
||||
|
||||
```
|
||||
$ USE_SSH=true yarn deploy
|
||||
```
|
||||
|
||||
Not using SSH:
|
||||
|
||||
```
|
||||
$ GIT_USER=<Your GitHub username> yarn deploy
|
||||
```
|
||||
|
||||
If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
|
@ -1,3 +0,0 @@
|
||||
module.exports = {
|
||||
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||
};
|
@ -1,5 +0,0 @@
|
||||
# Soon
|
||||
|
||||
```js
|
||||
// TODO: Add inforamtion about the api
|
||||
```
|
@ -1,94 +0,0 @@
|
||||
# API Usage
|
||||
|
||||
The reduced.to URL Shortener API allows you to shorten long URLs into shorter, more manageable ones. This documentation provides details on how to use the API effectively.
|
||||
|
||||
## API Base URL
|
||||
|
||||
The base URL for the API is `https://reduced.to/api/v1/shortener`.
|
||||
|
||||
## API Usage
|
||||
|
||||
| Method | Endpoint | Content-Type | Request Body | Description |
|
||||
| ------ | ----------------- | ---------------- | --------------------------------------- | -------------------------------------- |
|
||||
| POST | /api/v1/shortener | application/json | `{"originalUrl": "https://google.com"}` | Shortens a URL and returns the result. |
|
||||
|
||||
To shorten a URL, make a `POST` request to the base URL with the following JSON payload:
|
||||
|
||||
POST /api/v1/shortener
|
||||
Content-Type: application/json
|
||||
|
||||
```json
|
||||
{
|
||||
"originalUrl": "https://google.com"
|
||||
}
|
||||
```
|
||||
|
||||
Upon successful execution of the request, you will receive a JSON response containing the shortened URL. The response object will have the following structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"newUrl": "vv9ip"
|
||||
}
|
||||
```
|
||||
|
||||
## Javascript Example
|
||||
|
||||
```javascript
|
||||
// Define the API endpoint
|
||||
const url = 'https://reduced.to/api/v1/shortener';
|
||||
|
||||
// Define the original URL to be shortened
|
||||
const originalUrl = 'https://google.com';
|
||||
|
||||
// Create the request payload
|
||||
const payload = {
|
||||
originalUrl: originalUrl,
|
||||
};
|
||||
|
||||
// Send the POST request to shorten the URL
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(payload),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
// Extract the shortened URL from the response
|
||||
const shortenedUrl = data.newUrl;
|
||||
|
||||
// Print the shortened URL
|
||||
console.log(`Shortened URL: https://reduced.to/${shortenedUrl}`);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Error:', error.message);
|
||||
});
|
||||
```
|
||||
|
||||
# Python Example
|
||||
|
||||
```python
|
||||
import requests
|
||||
import json
|
||||
|
||||
# Define the API endpoint
|
||||
url = 'https://reduced.to/api/v1/shortener'
|
||||
|
||||
# Define the original URL to be shortened
|
||||
original_url = 'https://google.com'
|
||||
|
||||
# Create the request payload
|
||||
payload = {
|
||||
'originalUrl': original_url
|
||||
}
|
||||
|
||||
# Send the POST request to shorten the URL
|
||||
response = requests.post(url, json=payload)
|
||||
|
||||
# Extract the shortened URL from the response
|
||||
shortened_url = response.json()['newUrl']
|
||||
|
||||
# Print the shortened URL
|
||||
print(f'Shortened URL: https://reduced.to/{shortened_url}')
|
||||
```
|
@ -1,31 +0,0 @@
|
||||
---
|
||||
title: Overview
|
||||
description: Overview of the project
|
||||
hide_table_of_contents: true
|
||||
---
|
||||
|
||||
### What is Redcued.to?
|
||||
|
||||
Reduced.to is a modern web application designed to shorten link URLs, making them easier to remember, share, and track. The project is developed using cutting-edge technologies and services, including NestJS, Redis, Novu, PostgreSQL, and Qwik.
|
||||
|
||||
### How does it work?
|
||||
|
||||
Simply paste a long URL into the input field and click the "Shorten" button. The application will generate a shortened URL that you can copy and share with others. When a user clicks on the shortened URL, they will be redirected to the original long URL. There is also a dashboard for registered users to view their shortened URLs and track their performance.
|
||||
|
||||
### Technologies and Services
|
||||
|
||||
To ensure optimal performance, the project uses Redis as a cache storage mechanism. Redis is an efficient, in-memory data structure store that enables fast retrieval of key-value pairs. By leveraging Redis, we can store and retrieve mappings between short links and their corresponding long URLs in a highly efficient manner.
|
||||
|
||||
We uses NestJS as the backend framework for the project. NestJS is a progressive Node.js framework that offers a modular architecture and a powerful CLI. By leveraging NestJS, we can develop a robust backend that is easy to maintain and scale.
|
||||
|
||||
For data storage, we utilize PostgreSQL, a reliable and robust open-source relational database. PostgreSQL offers excellent data integrity and powerful querying capabilities, ensuring secure storage of user-related information while enabling efficient retrieval and manipulation as needed.
|
||||
|
||||
The frontend of the project is developed using Qwik, a cutting-edge framework known for its enhanced performance and scalability. By leveraging Qwik, we deliver a smooth and seamless user interface for an exceptional user experience.
|
||||
|
||||
In summary, the reduced.to project is a comprehensive URL shortener that incorporates various technologies and services to ensure optimal performance, security, and user experience. With the integration of Redis, Novu, PostgreSQL, and Qwik, we provide a reliable, efficient, and user-friendly platform for generating shortened URLs.
|
||||
|
||||
### Project Architecture Overview
|
||||
|
||||

|
||||
|
||||
Feel free to explore and contribute to this open-source project. We welcome new contributors and encourage you to join our community!
|
@ -1,87 +0,0 @@
|
||||
// @ts-check
|
||||
// Note: type annotations allow type checking and IDEs autocompletion
|
||||
|
||||
const lightCodeTheme = require('prism-react-renderer/themes/github');
|
||||
const darkCodeTheme = require('prism-react-renderer/themes/dracula');
|
||||
|
||||
/** @type {import('@docusaurus/types').Config} */
|
||||
const config = {
|
||||
title: 'Reduced.to',
|
||||
staticDirectories: ['public', 'static'],
|
||||
tagline:
|
||||
"Reduced.to is a modern web application that reduces the length of link URL, so it's easier to remember, share and track.",
|
||||
favicon: 'images/favicon.png',
|
||||
url: 'https://reduced.to/',
|
||||
baseUrl: '/',
|
||||
projectName: 'reduced.to',
|
||||
onBrokenLinks: 'throw',
|
||||
onBrokenMarkdownLinks: 'warn',
|
||||
i18n: {
|
||||
defaultLocale: 'en',
|
||||
locales: ['en'],
|
||||
},
|
||||
|
||||
presets: [
|
||||
[
|
||||
'classic',
|
||||
/** @type {import('@docusaurus/preset-classic').Options} */
|
||||
({
|
||||
docs: {
|
||||
sidebarPath: require.resolve('./sidebars.js'),
|
||||
// Please change this to your repo.
|
||||
// Remove this to remove the "edit this page" links.
|
||||
routeBasePath: '/',
|
||||
editUrl: '/',
|
||||
},
|
||||
|
||||
theme: {
|
||||
customCss: require.resolve('./src/css/custom.css'),
|
||||
},
|
||||
}),
|
||||
],
|
||||
],
|
||||
|
||||
themeConfig:
|
||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||
({
|
||||
navbar: {
|
||||
logo: {
|
||||
alt: 'Reduced.to Logo',
|
||||
src: `images/logo.svg`,
|
||||
//srcDark: `img/logo.svg`,
|
||||
href: '/',
|
||||
target: '_self',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
type: 'search',
|
||||
position: 'right',
|
||||
},
|
||||
{
|
||||
position: 'right',
|
||||
href: 'https://reduced.to',
|
||||
label: 'Visit App',
|
||||
},
|
||||
|
||||
{
|
||||
position: 'right',
|
||||
href: 'https://github.com/origranot/reduced.to',
|
||||
html: `
|
||||
<a aria-label="GitHub" class="navbar-github-link">
|
||||
<img src="images/github-logo.png" alt="GitHub Logo" class="navbar-github-logo" />
|
||||
</a>
|
||||
`,
|
||||
},
|
||||
],
|
||||
},
|
||||
footer: {
|
||||
copyright: `Copyright © ${new Date().getFullYear()} Reduced.to, Inc. Built with Docusaurus.`,
|
||||
},
|
||||
prism: {
|
||||
theme: lightCodeTheme,
|
||||
darkTheme: darkCodeTheme,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
module.exports = config;
|
12668
docs/package-lock.json
generated
12668
docs/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -1,50 +0,0 @@
|
||||
{
|
||||
"name": "my-website",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"fmt.check": "prettier --check .",
|
||||
"fmt": "prettier --write .",
|
||||
"docusaurus": "docusaurus",
|
||||
"start": "docusaurus start",
|
||||
"build": "docusaurus build",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
"deploy": "docusaurus deploy",
|
||||
"clear": "docusaurus clear",
|
||||
"serve": "docusaurus serve",
|
||||
"write-translations": "docusaurus write-translations",
|
||||
"write-heading-ids": "docusaurus write-heading-ids",
|
||||
"typecheck": "tsc",
|
||||
"lint": "prettier --list-different \"./src/**/*.{ts,tsx,json,md}\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "2.4.1",
|
||||
"@docusaurus/preset-classic": "2.4.1",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.2.1",
|
||||
"prism-react-renderer": "^1.3.5",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "2.4.1",
|
||||
"@tsconfig/docusaurus": "^1.0.5",
|
||||
"prettier": "^2.8.8",
|
||||
"typescript": "^4.7.4"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.5%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.14"
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
/**
|
||||
* Creating a sidebar enables you to:
|
||||
- create an ordered group of docs
|
||||
- render a sidebar for each doc of that group
|
||||
- provide next/previous navigation
|
||||
|
||||
The sidebars can be generated from the filesystem, or explicitly defined here.
|
||||
|
||||
Create as many sidebars as you want.
|
||||
*/
|
||||
|
||||
// @ts-check
|
||||
|
||||
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
||||
const sidebars = {
|
||||
tutorialSidebar: [
|
||||
'overview',
|
||||
{
|
||||
type: 'category',
|
||||
label: 'API',
|
||||
items: ['api/soon'],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
module.exports = sidebars;
|
@ -1,67 +0,0 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
type FeatureItem = {
|
||||
title: string;
|
||||
Svg: React.ComponentType<React.ComponentProps<'svg'>>;
|
||||
description: JSX.Element;
|
||||
};
|
||||
|
||||
const FeatureList: FeatureItem[] = [
|
||||
{
|
||||
title: 'Easy to Use',
|
||||
Svg: require('@site/static/img/undraw_docusaurus_mountain.svg').default,
|
||||
description: (
|
||||
<>Docusaurus was designed from the ground up to be easily installed and used to get your website up and running quickly.</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Focus on What Matters',
|
||||
Svg: require('@site/static/img/undraw_docusaurus_tree.svg').default,
|
||||
description: (
|
||||
<>
|
||||
Docusaurus lets you focus on your docs, and we'll do the chores. Go ahead and move your docs into the{' '}
|
||||
<code>docs</code> directory.
|
||||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Powered by React',
|
||||
Svg: require('@site/static/img/undraw_docusaurus_react.svg').default,
|
||||
description: (
|
||||
<>
|
||||
Extend or customize your website layout by reusing React. Docusaurus can be extended while reusing the same header and
|
||||
footer.
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
function Feature({ title, Svg, description }: FeatureItem) {
|
||||
return (
|
||||
<div className={clsx('col col--4')}>
|
||||
<div className="text--center">
|
||||
<Svg className={styles.featureSvg} role="img" />
|
||||
</div>
|
||||
<div className="text--center padding-horiz--md">
|
||||
<h3>{title}</h3>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function HomepageFeatures(): JSX.Element {
|
||||
return (
|
||||
<section className={styles.features}>
|
||||
<div className="container">
|
||||
<div className="row">
|
||||
{FeatureList.map((props, idx) => (
|
||||
<Feature key={idx} {...props} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user