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
15 changes: 15 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
node_modules
.git
.github
apps/backend/node_modules
apps/web/node_modules
apps/web/.svelte-kit
packages/shared/node_modules
dist
build
.env
.env.production
*.log
.gemini
.vscode
3D Objects
26 changes: 15 additions & 11 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
# ─── Database ───
DATABASE_URL=postgresql://devcard:devcard@localhost:5432/devcard?schema=public
# Connection string for PostgreSQL database
DATABASE_URL=postgresql://devcard:devcard@db:5432/devcard?schema=public

# ─── Redis ───
REDIS_URL=redis://localhost:6379
# Connection string for Redis instance
REDIS_URL=redis://redis:6379

# ─── JWT ───
# ─── Security ───
# Secret for signing JWTs
JWT_SECRET=your-super-secret-jwt-key-change-in-production

# ─── Encryption (for OAuth tokens) ───
# 32-byte hex string for encryption (e.g. OAuth tokens)
ENCRYPTION_KEY=your-32-byte-hex-encryption-key-here

# ─── GitHub OAuth ───
# ─── OAuth Providers (Optional for local dev) ───
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret

# ─── Google OAuth ───
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret

# ─── App URLs ───
# URL where the web frontend is accessible
PUBLIC_APP_URL=http://localhost:5173
BACKEND_URL=http://localhost:3000
# URL where the backend API is accessible
BACKEND_URL=http://localhost:3001
# URI to redirect to the mobile app
MOBILE_REDIRECT_URI=devcard://oauth/callback

# ─── Server ───
PORT=3000
# ─── Server Configuration ───
PORT=3001
HOST=0.0.0.0
NODE_ENV=development
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,25 @@ The following error cases are implemented:
| **Set Default Card** | 404 | `{ error: 'Card not found' }` — when card ID doesn't exist or doesn't belong to authenticated user |
| **Successful Deletion** | 204 | No content |

## Self-Hosting

DevCard is fully self-hostable via Docker Compose. You can run the entire stack (PostgreSQL, Redis, Backend, Web App) with a single command.

### Running with Docker Compose

1. Copy the example environment file:
```bash
cp .env.example .env
```
2. Review and update the `.env` file with your specific values (especially `JWT_SECRET` and `ENCRYPTION_KEY`).
3. Start the stack:
```bash
docker compose up -d
```
4. Access the web app at `http://localhost:5173` and the backend at `http://localhost:3001`.

The data for PostgreSQL and Redis is persisted in Docker volumes across restarts.

## Good First Issues

New to open source? We've got you covered! Check out our [Good First Issues](https://github.com/Dev-Card/DevCard/issues?q=is%3Aopen+label%3A%22good-first-issue%22), these are specially curated issues that are:
Expand Down
23 changes: 23 additions & 0 deletions apps/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM node:20-alpine AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /app

FROM base AS builder
COPY . .
RUN pnpm install --no-frozen-lockfile
RUN pnpm --filter @devcard/backend exec prisma generate
RUN pnpm --filter @devcard/shared build
RUN pnpm --filter @devcard/backend build

FROM base AS runner
# We copy the whole workspace for simplicity in a self-hosted mono-repo setup
# In a true enterprise setup, we'd prune the workspace, but this is sufficient.
COPY --from=builder /app /app
WORKDIR /app/apps/backend

EXPOSE 3001

# Apply migrations and start the server
CMD ["sh", "-c", "npx prisma migrate deploy && npm start"]
6 changes: 6 additions & 0 deletions apps/backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ import { analyticsRoutes } from './routes/analytics.js';

const __dirname = path.dirname(fileURLToPath(import.meta.url));

declare module 'fastify' {
interface FastifyInstance {
authenticate: any;
}
}

export async function buildApp() {
const app = Fastify({
logger: {
Expand Down
7 changes: 4 additions & 3 deletions apps/backend/src/routes/auth.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { encrypt } from '../utils/encryption.js';

const GITHUB_AUTH_URL = 'https://github.com/login/oauth/authorize';
const GITHUB_TOKEN_URL = 'https://github.com/login/oauth/access_token';
Expand Down Expand Up @@ -103,7 +104,7 @@ export async function authRoutes(app: FastifyInstance) {
});

// Save the authentication token for 'user:email read:user' so we have a basic platform connection
const encryptedToken = (app as any).encryption ? (app as any).encryption.encrypt(tokenData.access_token) : tokenData.access_token;
const encryptedToken = encrypt(tokenData.access_token);

await app.prisma.oAuthToken.upsert({
where: { userId_platform: { userId: user.id, platform: 'github' } },
Expand Down Expand Up @@ -134,7 +135,7 @@ export async function authRoutes(app: FastifyInstance) {

return reply.redirect(`${process.env.PUBLIC_APP_URL}/dashboard`);
} catch (err) {
app.log.error('GitHub auth error:', err);
app.log.error(err as any, 'GitHub auth error');
return reply.status(500).send({ error: 'Authentication failed' });
}
});
Expand Down Expand Up @@ -235,7 +236,7 @@ export async function authRoutes(app: FastifyInstance) {

return reply.redirect(`${process.env.PUBLIC_APP_URL}/dashboard`);
} catch (err) {
app.log.error('Google auth error:', err);
app.log.error(err as any, 'Google auth error');
return reply.status(500).send({ error: 'Authentication failed' });
}
});
Expand Down
5 changes: 3 additions & 2 deletions apps/backend/src/routes/connect.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { encrypt } from '../utils/encryption.js';

const GITHUB_AUTH_URL = 'https://github.com/login/oauth/authorize';
const GITHUB_TOKEN_URL = 'https://github.com/login/oauth/access_token';
Expand Down Expand Up @@ -95,7 +96,7 @@ export async function connectRoutes(app: FastifyInstance) {
}

// Encrypt and store the token
const encryptedToken = app.encryption.encrypt(tokenData.access_token);
const encryptedToken = encrypt(tokenData.access_token);

await app.prisma.oAuthToken.upsert({
where: {
Expand Down Expand Up @@ -125,7 +126,7 @@ export async function connectRoutes(app: FastifyInstance) {
return reply.redirect(`${process.env.PUBLIC_APP_URL}/settings?connected=github`);

} catch (err) {
app.log.error('GitHub connect error:', err);
app.log.error(err as any, 'GitHub connect error');
return reply.redirect(`${process.env.PUBLIC_APP_URL}/settings?error=server_error`);
}
});
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@
"isolatedModules": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
"exclude": ["node_modules", "dist", "src/__tests__"]
}
21 changes: 21 additions & 0 deletions apps/web/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
FROM node:20-alpine AS base
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
WORKDIR /app

FROM base AS builder
COPY . .
RUN pnpm install --no-frozen-lockfile
RUN pnpm --filter @devcard/shared build
RUN pnpm --filter @devcard/web build

FROM base AS runner
COPY --from=builder /app /app
WORKDIR /app/apps/web

EXPOSE 5173
ENV NODE_ENV=production
ENV PORT=5173

CMD ["node", "build/index.js"]
6 changes: 3 additions & 3 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@
"@devcard/shared": "workspace:*"
},
"devDependencies": {
"@sveltejs/adapter-auto": "^7.0.0",
"@sveltejs/adapter-node": "^5.2.9",
"@sveltejs/kit": "^2.50.2",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"@sveltejs/vite-plugin-svelte": "^4.0.0",
"svelte": "^5.51.0",
"svelte-check": "^4.4.2",
"typescript": "^5.9.3",
"vite": "^7.3.1"
"vite": "^5.4.21"
}
}
5 changes: 3 additions & 2 deletions apps/web/src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,16 @@
The open-source standard for developer networking. Put all your profiles—GitHub, LinkedIn, LeetCode, and more—into a single, high-impact digital card.
</p>
<div class="cta-group">
<a href="/create" class="btn btn-primary">⚡ Create Your Card</a>
<a
href="https://github.com/Dev-Card/DevCard"
class="btn-primary"
class="btn btn-secondary"
target="_blank"
rel="noopener"
>
⭐ Star on GitHub
</a>
<a href="/u/devcard-demo" class="btn-secondary">View Demo Profile →</a>
<a href="/u/devcard-demo" class="btn btn-secondary">View Demo Profile →</a>
</div>
</section>

Expand Down
Loading