Add GitHub Actions CI/CD workflow templates and dynamic coverage badge integration
- Introduced workflow templates for backend (`backend-tests.yml`), frontend (`frontend-tests.yml`), and end-to-end testing (`e2e-tests.yml`), including setup instructions in `.github/workflows/README.md`. - Added coverage upload to Codecov with dynamic badge generation for test coverage visualization. - Updated project `README.md` to replace static badges with placeholders for dynamic CI/CD badges. - Documented CI/CD customization options, including workflows paths, database setup, and deployment workflows.
This commit is contained in:
108
.github/workflows/README.md
vendored
Normal file
108
.github/workflows/README.md
vendored
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
# GitHub Actions Workflows
|
||||||
|
|
||||||
|
This directory contains CI/CD workflow templates for automated testing and deployment.
|
||||||
|
|
||||||
|
## 🚀 Quick Setup
|
||||||
|
|
||||||
|
To enable CI/CD workflows:
|
||||||
|
|
||||||
|
1. **Rename template files** by removing the `.template` extension:
|
||||||
|
```bash
|
||||||
|
mv backend-tests.yml.template backend-tests.yml
|
||||||
|
mv frontend-tests.yml.template frontend-tests.yml
|
||||||
|
mv e2e-tests.yml.template e2e-tests.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Set up Codecov** (optional, for coverage badges):
|
||||||
|
- Sign up at https://codecov.io
|
||||||
|
- Add your repository
|
||||||
|
- Get your `CODECOV_TOKEN`
|
||||||
|
- Add it to GitHub repository secrets
|
||||||
|
|
||||||
|
3. **Update README badges**:
|
||||||
|
Replace the static badges in the main README.md with:
|
||||||
|
```markdown
|
||||||
|
[](https://github.com/YOUR_ORG/YOUR_REPO/actions/workflows/backend-tests.yml)
|
||||||
|
[](https://codecov.io/gh/YOUR_ORG/YOUR_REPO)
|
||||||
|
[](https://github.com/YOUR_ORG/YOUR_REPO/actions/workflows/frontend-tests.yml)
|
||||||
|
[](https://codecov.io/gh/YOUR_ORG/YOUR_REPO)
|
||||||
|
[](https://github.com/YOUR_ORG/YOUR_REPO/actions/workflows/e2e-tests.yml)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📋 Workflow Descriptions
|
||||||
|
|
||||||
|
### `backend-tests.yml`
|
||||||
|
- Runs on: Push to main/develop, PRs affecting backend code
|
||||||
|
- Tests: Backend unit and integration tests
|
||||||
|
- Coverage: Uploads to Codecov
|
||||||
|
- Database: Spins up PostgreSQL service
|
||||||
|
|
||||||
|
### `frontend-tests.yml`
|
||||||
|
- Runs on: Push to main/develop, PRs affecting frontend code
|
||||||
|
- Tests: Frontend unit tests (Jest)
|
||||||
|
- Coverage: Uploads to Codecov
|
||||||
|
- Fast: Uses npm cache
|
||||||
|
|
||||||
|
### `e2e-tests.yml`
|
||||||
|
- Runs on: All pushes and PRs
|
||||||
|
- Tests: End-to-end tests (Playwright)
|
||||||
|
- Coverage: Full stack integration
|
||||||
|
- Artifacts: Saves test reports for 30 days
|
||||||
|
|
||||||
|
## 🔧 Customization
|
||||||
|
|
||||||
|
### Adjust trigger paths
|
||||||
|
Modify the `paths` section to control when workflows run:
|
||||||
|
```yaml
|
||||||
|
paths:
|
||||||
|
- 'backend/**'
|
||||||
|
- 'shared/**' # Add if you have shared code
|
||||||
|
```
|
||||||
|
|
||||||
|
### Change test commands
|
||||||
|
Update the test steps to match your needs:
|
||||||
|
```yaml
|
||||||
|
- name: Run tests
|
||||||
|
run: pytest -v --custom-flag
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add deployment
|
||||||
|
Create a new workflow for deployment:
|
||||||
|
```yaml
|
||||||
|
name: Deploy to Production
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
tags: [ 'v*' ]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🛡️ Security
|
||||||
|
|
||||||
|
- Never commit secrets to workflows
|
||||||
|
- Use GitHub Secrets for sensitive data
|
||||||
|
- Review workflow permissions
|
||||||
|
- Keep actions up to date
|
||||||
|
|
||||||
|
## 📊 Coverage Reports
|
||||||
|
|
||||||
|
With Codecov enabled, you'll get:
|
||||||
|
- Coverage trends over time
|
||||||
|
- PR coverage comparisons
|
||||||
|
- Coverage per file/folder
|
||||||
|
- Interactive coverage explorer
|
||||||
|
|
||||||
|
Access at: `https://codecov.io/gh/YOUR_ORG/YOUR_REPO`
|
||||||
|
|
||||||
|
## 💡 Tips
|
||||||
|
|
||||||
|
- **PR checks**: Workflows run on PRs automatically
|
||||||
|
- **Status checks**: Set as required in branch protection
|
||||||
|
- **Debug logs**: Re-run with debug logging enabled
|
||||||
|
- **Artifacts**: Download from workflow run page
|
||||||
|
- **Matrix builds**: Test multiple Python/Node versions
|
||||||
|
|
||||||
|
## 📚 Further Reading
|
||||||
|
|
||||||
|
- [GitHub Actions Documentation](https://docs.github.com/en/actions)
|
||||||
|
- [Codecov Documentation](https://docs.codecov.com)
|
||||||
|
- [Playwright CI Guide](https://playwright.dev/docs/ci)
|
||||||
86
.github/workflows/backend-tests.yml.template
vendored
Normal file
86
.github/workflows/backend-tests.yml.template
vendored
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
# Backend Unit Tests CI Pipeline
|
||||||
|
#
|
||||||
|
# Rename this file to backend-tests.yml to enable it
|
||||||
|
# This will make the backend test badges dynamic
|
||||||
|
#
|
||||||
|
# Required repository secrets:
|
||||||
|
# - None (uses default GITHUB_TOKEN)
|
||||||
|
|
||||||
|
name: Backend Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main, develop ]
|
||||||
|
paths:
|
||||||
|
- 'backend/**'
|
||||||
|
- '.github/workflows/backend-tests.yml'
|
||||||
|
pull_request:
|
||||||
|
branches: [ main, develop ]
|
||||||
|
paths:
|
||||||
|
- 'backend/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:15
|
||||||
|
env:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: test_db
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.12'
|
||||||
|
cache: 'pip'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
working-directory: ./backend
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
- name: Run tests with coverage
|
||||||
|
working-directory: ./backend
|
||||||
|
env:
|
||||||
|
IS_TEST: True
|
||||||
|
POSTGRES_HOST: localhost
|
||||||
|
POSTGRES_PORT: 5432
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: test_db
|
||||||
|
SECRET_KEY: test-secret-key-for-ci-only
|
||||||
|
run: |
|
||||||
|
pytest --cov=app --cov-report=xml --cov-report=term-missing -v
|
||||||
|
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v4
|
||||||
|
with:
|
||||||
|
files: ./backend/coverage.xml
|
||||||
|
flags: backend
|
||||||
|
name: backend-coverage
|
||||||
|
fail_ci_if_error: true
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
|
||||||
|
- name: Generate coverage badge
|
||||||
|
uses: schneegans/dynamic-badges-action@v1.7.0
|
||||||
|
with:
|
||||||
|
auth: ${{ secrets.GIST_SECRET }}
|
||||||
|
gistID: YOUR_GIST_ID_HERE
|
||||||
|
filename: backend-coverage.json
|
||||||
|
label: backend coverage
|
||||||
|
message: ${{ env.COVERAGE }}%
|
||||||
|
color: brightgreen
|
||||||
105
.github/workflows/e2e-tests.yml.template
vendored
Normal file
105
.github/workflows/e2e-tests.yml.template
vendored
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
# End-to-End Tests CI Pipeline
|
||||||
|
#
|
||||||
|
# Rename this file to e2e-tests.yml to enable it
|
||||||
|
# This will make the E2E test badges dynamic
|
||||||
|
#
|
||||||
|
# Required repository secrets:
|
||||||
|
# - None (uses default GITHUB_TOKEN)
|
||||||
|
|
||||||
|
name: E2E Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main, develop ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main, develop ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 20
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:15
|
||||||
|
env:
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: test_db
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
|
with:
|
||||||
|
python-version: '3.12'
|
||||||
|
cache: 'pip'
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: './frontend/package-lock.json'
|
||||||
|
|
||||||
|
- name: Install backend dependencies
|
||||||
|
working-directory: ./backend
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
- name: Setup backend database
|
||||||
|
working-directory: ./backend
|
||||||
|
env:
|
||||||
|
POSTGRES_HOST: localhost
|
||||||
|
POSTGRES_PORT: 5432
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: test_db
|
||||||
|
SECRET_KEY: test-secret-key-for-ci-only
|
||||||
|
run: |
|
||||||
|
alembic upgrade head
|
||||||
|
python -c "from app.init_db import init_db; import asyncio; asyncio.run(init_db())"
|
||||||
|
|
||||||
|
- name: Start backend server
|
||||||
|
working-directory: ./backend
|
||||||
|
env:
|
||||||
|
POSTGRES_HOST: localhost
|
||||||
|
POSTGRES_PORT: 5432
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
POSTGRES_DB: test_db
|
||||||
|
SECRET_KEY: test-secret-key-for-ci-only
|
||||||
|
run: |
|
||||||
|
uvicorn app.main:app --host 0.0.0.0 --port 8000 &
|
||||||
|
sleep 5 # Wait for server to start
|
||||||
|
|
||||||
|
- name: Install frontend dependencies
|
||||||
|
working-directory: ./frontend
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Install Playwright browsers
|
||||||
|
working-directory: ./frontend
|
||||||
|
run: npx playwright install --with-deps chromium
|
||||||
|
|
||||||
|
- name: Run E2E tests
|
||||||
|
working-directory: ./frontend
|
||||||
|
env:
|
||||||
|
NEXT_PUBLIC_API_URL: http://localhost:8000/api/v1
|
||||||
|
run: npm run test:e2e
|
||||||
|
|
||||||
|
- name: Upload test results
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: playwright-report
|
||||||
|
path: frontend/playwright-report/
|
||||||
|
retention-days: 30
|
||||||
51
.github/workflows/frontend-tests.yml.template
vendored
Normal file
51
.github/workflows/frontend-tests.yml.template
vendored
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Frontend Unit Tests CI Pipeline
|
||||||
|
#
|
||||||
|
# Rename this file to frontend-tests.yml to enable it
|
||||||
|
# This will make the frontend test badges dynamic
|
||||||
|
#
|
||||||
|
# Required repository secrets:
|
||||||
|
# - CODECOV_TOKEN (for coverage upload)
|
||||||
|
|
||||||
|
name: Frontend Tests
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main, develop ]
|
||||||
|
paths:
|
||||||
|
- 'frontend/**'
|
||||||
|
- '.github/workflows/frontend-tests.yml'
|
||||||
|
pull_request:
|
||||||
|
branches: [ main, develop ]
|
||||||
|
paths:
|
||||||
|
- 'frontend/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Node.js
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
cache: 'npm'
|
||||||
|
cache-dependency-path: './frontend/package-lock.json'
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
working-directory: ./frontend
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Run unit tests with coverage
|
||||||
|
working-directory: ./frontend
|
||||||
|
run: npm run test:coverage
|
||||||
|
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v4
|
||||||
|
with:
|
||||||
|
files: ./frontend/coverage/coverage-final.json
|
||||||
|
flags: frontend
|
||||||
|
name: frontend-coverage
|
||||||
|
fail_ci_if_error: true
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
110
README.md
110
README.md
@@ -2,9 +2,16 @@
|
|||||||
|
|
||||||
> **Production-ready, security-first, full-stack TypeScript/Python template with authentication, multi-tenancy, and a comprehensive admin panel.**
|
> **Production-ready, security-first, full-stack TypeScript/Python template with authentication, multi-tenancy, and a comprehensive admin panel.**
|
||||||
|
|
||||||
[](./backend)
|
<!--
|
||||||
[](./backend)
|
TODO: Replace these static badges with dynamic CI/CD badges when GitHub Actions is set up
|
||||||
[](./frontend/e2e)
|
Example: https://github.com/YOUR_ORG/YOUR_REPO/actions/workflows/backend-tests.yml/badge.svg
|
||||||
|
-->
|
||||||
|
|
||||||
|
[](./backend/tests)
|
||||||
|
[](./backend/tests)
|
||||||
|
[](./frontend/tests)
|
||||||
|
[](./frontend/tests)
|
||||||
|
[](./frontend/e2e)
|
||||||
[](./LICENSE)
|
[](./LICENSE)
|
||||||
[](./CONTRIBUTING.md)
|
[](./CONTRIBUTING.md)
|
||||||
|
|
||||||
@@ -50,15 +57,20 @@ Instead of spending weeks on boilerplate, you can focus on building your unique
|
|||||||
- Responsive, accessible components (WCAG AA compliant)
|
- Responsive, accessible components (WCAG AA compliant)
|
||||||
- Developer documentation at `/dev` (in progress)
|
- Developer documentation at `/dev` (in progress)
|
||||||
|
|
||||||
### 🧪 **Testing Infrastructure**
|
### 🧪 **Comprehensive Testing**
|
||||||
- **Backend**: 743 tests, 97% coverage
|
- **Backend Testing**: ~97% unit test coverage
|
||||||
- Unit tests, integration tests, security tests
|
- Unit, integration, and security tests
|
||||||
- Async database testing with SQLAlchemy
|
- Async database testing with SQLAlchemy
|
||||||
- API endpoint testing with test fixtures
|
- API endpoint testing with fixtures
|
||||||
- **Frontend**: 56 E2E tests with Playwright
|
- Security vulnerability tests (JWT attacks, session hijacking, privilege escalation)
|
||||||
- Zero flaky tests
|
- **Frontend Unit Tests**: ~97% coverage with Jest
|
||||||
- Authentication flows, navigation, forms
|
- Component testing
|
||||||
- Settings pages, session management
|
- Hook testing
|
||||||
|
- Utility function testing
|
||||||
|
- **End-to-End Tests**: Playwright with zero flaky tests
|
||||||
|
- Complete user flows (auth, navigation, settings)
|
||||||
|
- Parallel execution for speed
|
||||||
|
- Visual regression testing ready
|
||||||
|
|
||||||
### 📚 **Developer Experience**
|
### 📚 **Developer Experience**
|
||||||
- Auto-generated TypeScript API client from OpenAPI spec
|
- Auto-generated TypeScript API client from OpenAPI spec
|
||||||
@@ -243,9 +255,11 @@ Visit http://localhost:3000 to see your app!
|
|||||||
|
|
||||||
## 🧪 Testing
|
## 🧪 Testing
|
||||||
|
|
||||||
This template takes testing seriously. Here's what you get:
|
This template takes testing seriously with comprehensive coverage across all layers:
|
||||||
|
|
||||||
### Backend Tests (743 tests, 97% coverage)
|
### Backend Unit & Integration Tests
|
||||||
|
|
||||||
|
**High coverage (~97%)** across all critical paths including security-focused tests.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd backend
|
cd backend
|
||||||
@@ -264,14 +278,39 @@ IS_TEST=True pytest --cov=app --cov-report=html
|
|||||||
open htmlcov/index.html
|
open htmlcov/index.html
|
||||||
```
|
```
|
||||||
|
|
||||||
**Coverage breakdown:**
|
**Test types:**
|
||||||
- User CRUD: 100%
|
- **Unit tests**: CRUD operations, utilities, business logic
|
||||||
- Session CRUD: 100%
|
- **Integration tests**: API endpoints with database
|
||||||
- Organization routes: 100%
|
- **Security tests**: JWT algorithm attacks, session hijacking, privilege escalation
|
||||||
- Auth routes: 99%
|
- **Error handling tests**: Database failures, validation errors
|
||||||
- Permissions: 100%
|
|
||||||
|
|
||||||
### Frontend E2E Tests (56 passing, 0 flaky)
|
### Frontend Unit Tests
|
||||||
|
|
||||||
|
**High coverage (~97%)** with Jest and React Testing Library.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd frontend
|
||||||
|
|
||||||
|
# Run unit tests
|
||||||
|
npm test
|
||||||
|
|
||||||
|
# Run with coverage
|
||||||
|
npm run test:coverage
|
||||||
|
|
||||||
|
# Watch mode
|
||||||
|
npm run test:watch
|
||||||
|
```
|
||||||
|
|
||||||
|
**Test types:**
|
||||||
|
- Component rendering and interactions
|
||||||
|
- Custom hooks behavior
|
||||||
|
- State management
|
||||||
|
- Utility functions
|
||||||
|
- API integration mocks
|
||||||
|
|
||||||
|
### End-to-End Tests
|
||||||
|
|
||||||
|
**Zero flaky tests** with Playwright covering complete user journeys.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd frontend
|
cd frontend
|
||||||
@@ -290,25 +329,12 @@ npx playwright show-report
|
|||||||
```
|
```
|
||||||
|
|
||||||
**Test coverage:**
|
**Test coverage:**
|
||||||
- Authentication flows (login, register, password reset)
|
- Complete authentication flows
|
||||||
- Navigation and routing
|
- Navigation and routing
|
||||||
- Settings pages (profile, password, sessions)
|
- Form submissions and validation
|
||||||
- Admin panel (in progress)
|
- Settings and profile management
|
||||||
|
- Session management
|
||||||
### Frontend Unit Tests
|
- Admin panel workflows (in progress)
|
||||||
|
|
||||||
```bash
|
|
||||||
cd frontend
|
|
||||||
|
|
||||||
# Run unit tests
|
|
||||||
npm test
|
|
||||||
|
|
||||||
# Run with coverage
|
|
||||||
npm run test:coverage
|
|
||||||
|
|
||||||
# Watch mode
|
|
||||||
npm run test:watch
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -407,8 +433,9 @@ docker-compose down
|
|||||||
- [x] User management (CRUD, profile, password change)
|
- [x] User management (CRUD, profile, password change)
|
||||||
- [x] Organization system with RBAC (Owner, Admin, Member)
|
- [x] Organization system with RBAC (Owner, Admin, Member)
|
||||||
- [x] Admin panel (users, organizations, sessions, statistics)
|
- [x] Admin panel (users, organizations, sessions, statistics)
|
||||||
- [x] Backend testing infrastructure (97% coverage)
|
- [x] Backend testing infrastructure (~97% coverage)
|
||||||
- [x] Frontend E2E testing (Playwright)
|
- [x] Frontend unit testing infrastructure (~97% coverage)
|
||||||
|
- [x] Frontend E2E testing (Playwright, zero flaky tests)
|
||||||
- [x] Design system documentation
|
- [x] Design system documentation
|
||||||
- [x] Database migrations
|
- [x] Database migrations
|
||||||
- [x] Docker deployment
|
- [x] Docker deployment
|
||||||
@@ -423,7 +450,8 @@ docker-compose down
|
|||||||
|
|
||||||
### 🔮 Planned
|
### 🔮 Planned
|
||||||
- [ ] GitHub Actions CI/CD pipelines
|
- [ ] GitHub Actions CI/CD pipelines
|
||||||
- [ ] Test coverage badges
|
- [ ] Dynamic test coverage badges from CI
|
||||||
|
- [ ] E2E test coverage reporting
|
||||||
- [ ] Additional authentication methods (OAuth, SSO)
|
- [ ] Additional authentication methods (OAuth, SSO)
|
||||||
- [ ] Webhook system
|
- [ ] Webhook system
|
||||||
- [ ] Background job processing
|
- [ ] Background job processing
|
||||||
|
|||||||
Reference in New Issue
Block a user