Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
name: Bug Report
about: Report a bug or unexpected behavior
title: '[Bug] '
labels: bug
assignees: ''
---

## Description
A clear and concise description of what the bug is.

## Steps to Reproduce
1. Go to '...'
2. Click on '...'
3. See error

## Expected Behavior
What you expected to happen.

## Actual Behavior
What actually happened.

## Environment
- OS: (e.g. Windows 11, Ubuntu 22.04, macOS 14)
- Browser: (e.g. Chrome 123, Firefox 124)
- .NET version: (run `dotnet --version`)

## Additional Context
Screenshots, logs, or any other context about the problem.
19 changes: 19 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
name: Feature Request
about: Suggest a new feature or improvement
title: '[Feature] '
labels: enhancement
assignees: ''
---

## Problem Statement
What problem does this feature solve? Who is affected?

## Proposed Solution
Describe the solution you'd like to see.

## Alternatives Considered
Any alternative solutions or features you've considered.

## Additional Context
Mockups, examples, or any other context about the feature request.
13 changes: 13 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
version: 2
updates:
- package-ecosystem: nuget
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 10

- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
open-pull-requests-limit: 5
86 changes: 86 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
build-and-test:
name: Build & Test
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'

- name: Restore dependencies
run: dotnet restore SnipLink.sln

- name: Build
run: dotnet build SnipLink.sln --no-restore -c Release

- name: Run tests
run: |
dotnet test src/SnipLink.Tests/SnipLink.Tests.csproj \
--no-build -c Release \
--collect:"XPlat Code Coverage" \
--results-directory ./coverage \
--logger "trx;LogFileName=results.trx"

- name: Upload test results
uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: coverage/

codeql:
name: CodeQL Analysis
runs-on: ubuntu-latest
if: github.event_name == 'push'
permissions:
security-events: write
actions: read
contents: read

steps:
- uses: actions/checkout@v4

- uses: github/codeql-action/init@v3
with:
languages: csharp

- uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'

- name: Build for CodeQL
run: dotnet build SnipLink.sln -c Release

- uses: github/codeql-action/analyze@v3

docker:
name: Docker Smoke Test
runs-on: ubuntu-latest
needs: build-and-test
if: github.event_name == 'push'

steps:
- uses: actions/checkout@v4

- name: Build Docker image
run: docker build -t sniplink-api .

- name: Run container
run: docker run -d -p 8080:8080 sniplink-api

- name: Wait for startup
run: sleep 10

- name: Health check
run: curl -f http://localhost:8080/healthz
91 changes: 91 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
## ── Claude Code / Anthropic AI tooling ──────────────────────────────────────
.claude/
CLAUDE.md
.claudeignore
claude.json

## ── .NET ─────────────────────────────────────────────────────────────────────
# Build results
[Dd]ebug/
[Rr]elease/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/

# Visual Studio
.vs/
*.user
*.suo
*.userosscache
*.sln.docstates
*.userprefs

# Rider
.idea/
*.sln.iml

# VS Code
.vscode/
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json

# NuGet
*.nupkg
*.snupkg
**/[Pp]ackages/
!**/[Pp]ackages/build/
*.nuget.props
*.nuget.targets
project.lock.json
project.fragment.lock.json
artifacts/

# dotnet tools manifest (keep .config/dotnet-tools.json but not local tool installs)
.dotnet/

# Test results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*.VisualState.xml
TestResult.xml
nunit-*.xml
coverage*.xml
coverage*.json
coverage*.info
*.coveragexml
*.coverage

# ASP.NET Scaffolding
ScaffoldingReadMe.txt

## ── Secrets / Environment ────────────────────────────────────────────────────
appsettings.Development.json
appsettings.Production.json
appsettings.Staging.json
appsettings.Local.json
**/secrets.json
**/*.pfx
**/*.p12
.env
.env.*
!.env.example

## ── OS ───────────────────────────────────────────────────────────────────────
.DS_Store
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/

## ── Docker ───────────────────────────────────────────────────────────────────
# Keep Dockerfiles; ignore local compose overrides
docker-compose.override.yml
39 changes: 39 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# ── Stage 1: Build ───────────────────────────────────────────────────────────
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src

# Copy project files first for layer caching — restore only re-runs when these change
COPY SnipLink.sln .
COPY src/SnipLink.Api/SnipLink.Api.csproj src/SnipLink.Api/
COPY src/SnipLink.Blazor/SnipLink.Blazor.csproj src/SnipLink.Blazor/
COPY src/SnipLink.Shared/SnipLink.Shared.csproj src/SnipLink.Shared/
COPY src/SnipLink.Tests/SnipLink.Tests.csproj src/SnipLink.Tests/

RUN dotnet restore SnipLink.sln

COPY src/ src/

RUN dotnet publish src/SnipLink.Api/SnipLink.Api.csproj \
-c Release -o /app/publish --no-restore

# ── Stage 2: Runtime ─────────────────────────────────────────────────────────
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine AS final
WORKDIR /app

# Non-root user for security
RUN adduser -D appuser && mkdir -p /app/data && chown -R appuser:appuser /app

COPY --from=build --chown=appuser:appuser /app/publish .

# SQLite database lives on a named volume so it persists across container restarts
VOLUME /app/data

ENV ASPNETCORE_URLS=http://+:8080
EXPOSE 8080

HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
CMD wget -qO- http://localhost:8080/healthz || exit 1

USER appuser

ENTRYPOINT ["dotnet", "SnipLink.Api.dll"]
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 daniyal malik

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.
39 changes: 39 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
services:
api:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
volumes:
- sniplink-data:/app/data
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ConnectionStrings__DefaultConnection=Data Source=/app/data/sniplink.db
- Analytics__IpHashSalt=${ANALYTICS_IP_HASH_SALT:?Set ANALYTICS_IP_HASH_SALT in .env}
- Cors__AllowedOrigin=http://localhost:8081
- Frontend__BaseUrl=http://localhost:8081
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:8080/healthz"]
interval: 30s
timeout: 10s
retries: 3
start_period: 15s
restart: unless-stopped

blazor:
build:
context: .
dockerfile: src/SnipLink.Blazor/Dockerfile
ports:
- "8081:8080"
environment:
- ASPNETCORE_ENVIRONMENT=Production
- ApiBaseUrl=http://api:8080
depends_on:
api:
condition: service_healthy
restart: unless-stopped

volumes:
sniplink-data:
Loading
Loading