@insureco/cli 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/dist/commands/deploy.d.ts.map +1 -1
  2. package/dist/commands/deploy.js +6 -52
  3. package/dist/commands/deploy.js.map +1 -1
  4. package/dist/commands/git.d.ts.map +1 -1
  5. package/dist/commands/git.js +22 -8
  6. package/dist/commands/git.js.map +1 -1
  7. package/dist/commands/login.d.ts +1 -0
  8. package/dist/commands/login.d.ts.map +1 -1
  9. package/dist/commands/login.js +94 -4
  10. package/dist/commands/login.js.map +1 -1
  11. package/dist/commands/push.d.ts.map +1 -1
  12. package/dist/commands/push.js +10 -1
  13. package/dist/commands/push.js.map +1 -1
  14. package/dist/commands/rollback.d.ts.map +1 -1
  15. package/dist/commands/rollback.js +10 -35
  16. package/dist/commands/rollback.js.map +1 -1
  17. package/dist/commands/sample.d.ts +2 -0
  18. package/dist/commands/sample.d.ts.map +1 -1
  19. package/dist/commands/sample.js +140 -12
  20. package/dist/commands/sample.js.map +1 -1
  21. package/dist/commands/versions.d.ts.map +1 -1
  22. package/dist/commands/versions.js +10 -56
  23. package/dist/commands/versions.js.map +1 -1
  24. package/dist/index.js +5 -2
  25. package/dist/index.js.map +1 -1
  26. package/dist/lib/builder.d.ts.map +1 -1
  27. package/dist/lib/builder.js +46 -3
  28. package/dist/lib/builder.js.map +1 -1
  29. package/dist/lib/forgejo.d.ts +19 -2
  30. package/dist/lib/forgejo.d.ts.map +1 -1
  31. package/dist/lib/forgejo.js +143 -8
  32. package/dist/lib/forgejo.js.map +1 -1
  33. package/dist/lib/watch.d.ts +26 -0
  34. package/dist/lib/watch.d.ts.map +1 -0
  35. package/dist/lib/watch.js +136 -0
  36. package/dist/lib/watch.js.map +1 -0
  37. package/dist/templates/nextjs-oauth/.env.example +14 -0
  38. package/dist/templates/nextjs-oauth/Dockerfile +51 -0
  39. package/dist/templates/nextjs-oauth/README.md +128 -0
  40. package/dist/templates/nextjs-oauth/catalog-info.yaml +17 -0
  41. package/dist/templates/nextjs-oauth/helm/{{name}}/Chart.yaml +9 -0
  42. package/dist/templates/nextjs-oauth/helm/{{name}}/templates/deployment.yaml +68 -0
  43. package/dist/templates/nextjs-oauth/helm/{{name}}/templates/service.yaml +17 -0
  44. package/dist/templates/nextjs-oauth/helm/{{name}}/values.yaml +51 -0
  45. package/dist/templates/nextjs-oauth/next.config.js +23 -0
  46. package/dist/templates/nextjs-oauth/package.json +30 -0
  47. package/dist/templates/nextjs-oauth/public/.gitkeep +0 -0
  48. package/dist/templates/nextjs-oauth/src/app/api/auth/callback/route.ts +64 -0
  49. package/dist/templates/nextjs-oauth/src/app/api/auth/login/route.ts +16 -0
  50. package/dist/templates/nextjs-oauth/src/app/api/auth/logout/route.ts +9 -0
  51. package/dist/templates/nextjs-oauth/src/app/api/auth/session/route.ts +40 -0
  52. package/dist/templates/nextjs-oauth/src/app/api/example/route.ts +63 -0
  53. package/dist/templates/nextjs-oauth/src/app/api/health/route.ts +10 -0
  54. package/dist/templates/nextjs-oauth/src/app/dashboard/page.tsx +92 -0
  55. package/dist/templates/nextjs-oauth/src/app/layout.tsx +18 -0
  56. package/dist/templates/nextjs-oauth/src/app/page.tsx +110 -0
  57. package/dist/templates/nextjs-oauth/src/lib/auth.ts +274 -0
  58. package/dist/templates/nextjs-oauth/src/middleware.ts +70 -0
  59. package/dist/templates/nextjs-oauth/tsconfig.json +26 -0
  60. package/package.json +1 -1
@@ -1,10 +1,64 @@
1
1
  import { homedir } from 'node:os';
2
2
  import { join } from 'node:path';
3
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
3
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'node:fs';
4
+ import { randomBytes, createHash } from 'node:crypto';
4
5
  const FORGEJO_URL = 'https://git.insureco.io';
5
6
  const FORGEJO_ORG = 'insureco';
7
+ const FORGEJO_OAUTH_CLIENT_ID = '01b2211c-35f9-46a1-b71e-4890bf821055';
6
8
  const CONFIG_DIR = join(homedir(), '.iec');
7
9
  const CREDENTIALS_FILE = join(CONFIG_DIR, 'forgejo-credentials.json');
10
+ // PKCE utilities for OAuth2 public clients
11
+ export function generateCodeVerifier() {
12
+ return randomBytes(32).toString('base64url');
13
+ }
14
+ export function generateCodeChallenge(verifier) {
15
+ return createHash('sha256').update(verifier).digest('base64url');
16
+ }
17
+ export function generateState() {
18
+ return randomBytes(16).toString('hex');
19
+ }
20
+ export function buildForgejoAuthUrl(codeChallenge, redirectUri, state) {
21
+ const params = new URLSearchParams({
22
+ client_id: FORGEJO_OAUTH_CLIENT_ID,
23
+ redirect_uri: redirectUri,
24
+ response_type: 'code',
25
+ code_challenge: codeChallenge,
26
+ code_challenge_method: 'S256',
27
+ state,
28
+ });
29
+ return `${FORGEJO_URL}/login/oauth/authorize?${params.toString()}`;
30
+ }
31
+ export async function exchangeCodeForToken(code, codeVerifier, redirectUri) {
32
+ const response = await fetch(`${FORGEJO_URL}/login/oauth/access_token`, {
33
+ method: 'POST',
34
+ headers: {
35
+ 'Content-Type': 'application/x-www-form-urlencoded',
36
+ 'Accept': 'application/json',
37
+ },
38
+ body: new URLSearchParams({
39
+ grant_type: 'authorization_code',
40
+ client_id: FORGEJO_OAUTH_CLIENT_ID,
41
+ code,
42
+ redirect_uri: redirectUri,
43
+ code_verifier: codeVerifier,
44
+ }),
45
+ });
46
+ if (!response.ok) {
47
+ const text = await response.text();
48
+ throw new Error(`Token exchange failed: ${text}`);
49
+ }
50
+ const data = (await response.json());
51
+ if (!data.access_token) {
52
+ throw new Error('No access token in response');
53
+ }
54
+ return {
55
+ url: FORGEJO_URL,
56
+ token: data.access_token,
57
+ tokenType: 'oauth2',
58
+ refreshToken: data.refresh_token,
59
+ expiresAt: new Date(Date.now() + data.expires_in * 1000).toISOString(),
60
+ };
61
+ }
8
62
  function loadCredentials() {
9
63
  if (!existsSync(CREDENTIALS_FILE)) {
10
64
  return null;
@@ -26,33 +80,93 @@ function saveCredentials(credentials) {
26
80
  export class ForgejoClient {
27
81
  baseUrl;
28
82
  token;
29
- constructor(url, token) {
83
+ tokenType;
84
+ refreshToken;
85
+ expiresAt;
86
+ constructor(url, token, tokenType = 'pat', refreshToken, expiresAt) {
30
87
  this.baseUrl = url.replace(/\/$/, '');
31
88
  this.token = token;
89
+ this.tokenType = tokenType;
90
+ this.refreshToken = refreshToken;
91
+ this.expiresAt = expiresAt;
92
+ }
93
+ isExpired() {
94
+ if (!this.expiresAt)
95
+ return false;
96
+ return new Date(this.expiresAt).getTime() < Date.now() - 60_000;
97
+ }
98
+ async tryRefresh() {
99
+ if (this.tokenType !== 'oauth2' || !this.refreshToken) {
100
+ return false;
101
+ }
102
+ try {
103
+ const response = await fetch(`${this.baseUrl}/login/oauth/access_token`, {
104
+ method: 'POST',
105
+ headers: {
106
+ 'Content-Type': 'application/x-www-form-urlencoded',
107
+ 'Accept': 'application/json',
108
+ },
109
+ body: new URLSearchParams({
110
+ grant_type: 'refresh_token',
111
+ client_id: FORGEJO_OAUTH_CLIENT_ID,
112
+ refresh_token: this.refreshToken,
113
+ }),
114
+ });
115
+ if (!response.ok)
116
+ return false;
117
+ const data = (await response.json());
118
+ if (!data.access_token)
119
+ return false;
120
+ const newToken = data.access_token;
121
+ const newRefreshToken = data.refresh_token;
122
+ const newExpiresAt = new Date(Date.now() + data.expires_in * 1000).toISOString();
123
+ // Persist to disk first — if this throws, in-memory state stays unchanged
124
+ saveCredentials({
125
+ url: this.baseUrl,
126
+ token: newToken,
127
+ tokenType: 'oauth2',
128
+ refreshToken: newRefreshToken,
129
+ expiresAt: newExpiresAt,
130
+ });
131
+ this.token = newToken;
132
+ this.refreshToken = newRefreshToken;
133
+ this.expiresAt = newExpiresAt;
134
+ return true;
135
+ }
136
+ catch {
137
+ return false;
138
+ }
32
139
  }
33
140
  static async create() {
34
141
  const creds = loadCredentials();
35
142
  if (!creds) {
36
- throw new Error('Not authenticated with Forgejo. Run: iec git login');
143
+ throw new Error('Not authenticated with git.insureco.io. Run: iec login');
37
144
  }
38
- return new ForgejoClient(creds.url, creds.token);
145
+ return new ForgejoClient(creds.url, creds.token, creds.tokenType || 'pat', creds.refreshToken, creds.expiresAt);
39
146
  }
40
- static saveCredentials(url, token) {
41
- saveCredentials({ url, token });
147
+ static saveCredentials(url, token, tokenType = 'pat', refreshToken, expiresAt) {
148
+ saveCredentials({ url, token, tokenType, refreshToken, expiresAt });
42
149
  }
43
150
  static hasCredentials() {
44
151
  return loadCredentials() !== null;
45
152
  }
46
153
  static clearCredentials() {
47
154
  if (existsSync(CREDENTIALS_FILE)) {
48
- const { unlinkSync } = require('node:fs');
49
155
  unlinkSync(CREDENTIALS_FILE);
50
156
  }
51
157
  }
52
158
  async request(method, path, body) {
159
+ // Auto-refresh expired OAuth2 tokens before making the request
160
+ let didRefresh = false;
161
+ if (this.tokenType === 'oauth2' && this.isExpired()) {
162
+ didRefresh = await this.tryRefresh();
163
+ }
164
+ const authHeader = this.tokenType === 'oauth2'
165
+ ? `Bearer ${this.token}`
166
+ : `token ${this.token}`;
53
167
  const url = `${this.baseUrl}/api/v1${path}`;
54
168
  const headers = {
55
- 'Authorization': `token ${this.token}`,
169
+ 'Authorization': authHeader,
56
170
  'Accept': 'application/json',
57
171
  };
58
172
  if (body) {
@@ -64,6 +178,27 @@ export class ForgejoClient {
64
178
  headers,
65
179
  body: body ? JSON.stringify(body) : undefined,
66
180
  });
181
+ // If 401 with OAuth2 and we haven't already refreshed, try once
182
+ if (response.status === 401 && this.tokenType === 'oauth2' && !didRefresh) {
183
+ const refreshed = await this.tryRefresh();
184
+ if (refreshed) {
185
+ const retryHeaders = {
186
+ ...headers,
187
+ 'Authorization': `Bearer ${this.token}`,
188
+ };
189
+ const retryResponse = await fetch(url, {
190
+ method,
191
+ headers: retryHeaders,
192
+ body: body ? JSON.stringify(body) : undefined,
193
+ });
194
+ if (retryResponse.ok) {
195
+ if (retryResponse.status === 204)
196
+ return { success: true };
197
+ const data = (await retryResponse.json());
198
+ return { success: true, data };
199
+ }
200
+ }
201
+ }
67
202
  if (!response.ok) {
68
203
  const text = await response.text();
69
204
  let errorMessage = `HTTP ${response.status}`;
@@ -1 +1 @@
1
- {"version":3,"file":"forgejo.js","sourceRoot":"","sources":["../../src/lib/forgejo.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"forgejo.js","sourceRoot":"","sources":["../../src/lib/forgejo.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { BuilderClient } from './builder.js';
2
+ interface WatchOptions {
3
+ /** Max consecutive transient errors before giving up (default: 10) */
4
+ maxRetries?: number;
5
+ /** Base poll interval in ms (default: 3000) */
6
+ pollInterval?: number;
7
+ /** Max poll interval in ms during backoff (default: 15000) */
8
+ maxPollInterval?: number;
9
+ /** Max total watch duration in ms (default: 30 minutes) */
10
+ timeout?: number;
11
+ /** Stream build logs incrementally (default: true) */
12
+ streamLogs?: boolean;
13
+ /** Label for terminal output — e.g. "Build", "Deployment", "Rollback" */
14
+ label?: string;
15
+ }
16
+ export declare class WatchError extends Error {
17
+ readonly buildId: string;
18
+ constructor(message: string, buildId: string);
19
+ }
20
+ /**
21
+ * Watch a build's progress with retry logic, backoff, and incremental log streaming.
22
+ * Throws WatchError on timeout, max retries, or build failure.
23
+ */
24
+ export declare function watchBuild(builder: BuilderClient, buildId: string, options?: WatchOptions): Promise<void>;
25
+ export {};
26
+ //# sourceMappingURL=watch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.d.ts","sourceRoot":"","sources":["../../src/lib/watch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAmBjD,UAAU,YAAY;IACpB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,8DAA8D;IAC9D,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,sDAAsD;IACtD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,yEAAyE;IACzE,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,qBAAa,UAAW,SAAQ,KAAK;aACU,OAAO,EAAE,MAAM;gBAAhD,OAAO,EAAE,MAAM,EAAkB,OAAO,EAAE,MAAM;CAI7D;AAMD;;;GAGG;AACH,wBAAsB,UAAU,CAC9B,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,YAAiB,GACzB,OAAO,CAAC,IAAI,CAAC,CAgGf"}
@@ -0,0 +1,136 @@
1
+ import { log } from './output.js';
2
+ const TERMINAL_STATES = ['completed', 'failed', 'cancelled'];
3
+ const STATUS_ICONS = {
4
+ queued: '\u23f3', // hourglass
5
+ cloning: '\ud83d\udce5', // inbox
6
+ building: '\ud83d\udd28', // hammer
7
+ pushing: '\ud83d\udce4', // outbox
8
+ deploying: '\ud83d\ude80', // rocket
9
+ completed: '\u2705', // check
10
+ failed: '\u274c', // x
11
+ cancelled: '\u26d4', // no entry
12
+ };
13
+ export class WatchError extends Error {
14
+ buildId;
15
+ constructor(message, buildId) {
16
+ super(message);
17
+ this.buildId = buildId;
18
+ this.name = 'WatchError';
19
+ }
20
+ }
21
+ function isTransientError(code) {
22
+ return code === 'NON_JSON_RESPONSE' || code === 'HTTP_ERROR';
23
+ }
24
+ /**
25
+ * Watch a build's progress with retry logic, backoff, and incremental log streaming.
26
+ * Throws WatchError on timeout, max retries, or build failure.
27
+ */
28
+ export async function watchBuild(builder, buildId, options = {}) {
29
+ const { maxRetries = 10, pollInterval = 3000, maxPollInterval = 15000, timeout = 30 * 60 * 1000, streamLogs = true, label = 'Build', } = options;
30
+ let lastStatus = '';
31
+ let consecutiveErrors = 0;
32
+ let currentInterval = pollInterval;
33
+ let logLinesSeen = 0;
34
+ const startTime = Date.now();
35
+ while (true) {
36
+ // Timeout guard
37
+ if (Date.now() - startTime > timeout) {
38
+ log.error(`${label} watch timed out after ${Math.round(timeout / 60000)} minutes`);
39
+ log.dim(` The build may still be running. Check status with:`);
40
+ log.dim(` iec status --build ${buildId}`);
41
+ throw new WatchError('Watch timed out', buildId);
42
+ }
43
+ const result = await builder.getBuild(buildId);
44
+ if (!result.success || !result.data) {
45
+ consecutiveErrors++;
46
+ const errMsg = result.error?.message || 'Unknown error';
47
+ if (consecutiveErrors >= maxRetries) {
48
+ log.error(`${label} watch failed after ${maxRetries} consecutive errors`);
49
+ log.dim(` Last error: ${errMsg}`);
50
+ log.dim(` The build may still be running. Check status with:`);
51
+ log.dim(` iec status --build ${buildId}`);
52
+ throw new WatchError(`Failed after ${maxRetries} consecutive errors: ${errMsg}`, buildId);
53
+ }
54
+ if (isTransientError(result.error?.code)) {
55
+ // Backoff on transient errors (Cloudflare HTML pages, 502s, etc.)
56
+ currentInterval = Math.min(currentInterval * 1.5, maxPollInterval);
57
+ log.warning(`${label} status unavailable (${consecutiveErrors}/${maxRetries}), retrying...`);
58
+ log.dim(` ${errMsg}`);
59
+ }
60
+ else {
61
+ throw new WatchError(`Failed to get build status: ${errMsg}`, buildId);
62
+ }
63
+ await delay(currentInterval);
64
+ continue;
65
+ }
66
+ // Successful response — reset error state and interval
67
+ consecutiveErrors = 0;
68
+ currentInterval = pollInterval;
69
+ const build = result.data;
70
+ // Print status transition
71
+ if (build.status !== lastStatus) {
72
+ lastStatus = build.status;
73
+ const icon = STATUS_ICONS[build.status] || '\u2022';
74
+ log.info(`${icon} ${build.status}`);
75
+ }
76
+ // Stream incremental logs
77
+ if (streamLogs) {
78
+ logLinesSeen = await printNewLogs(builder, buildId, logLinesSeen);
79
+ }
80
+ // Check for terminal state
81
+ if (TERMINAL_STATES.includes(build.status)) {
82
+ log.info('');
83
+ if (build.status === 'completed') {
84
+ log.success(`${label} completed successfully!`);
85
+ if (build.imageTag) {
86
+ log.dim(` Image: ${build.imageTag}`);
87
+ }
88
+ return;
89
+ }
90
+ else if (build.status === 'cancelled') {
91
+ log.warning(`${label} was cancelled`);
92
+ return;
93
+ }
94
+ else {
95
+ log.error(`${label} failed`);
96
+ if (build.error) {
97
+ log.dim(` Error: ${build.error}`);
98
+ }
99
+ log.info('');
100
+ log.info('View full logs:');
101
+ log.dim(` iec logs --build ${buildId}`);
102
+ throw new WatchError(`${label} failed: ${build.error || 'Unknown error'}`, buildId);
103
+ }
104
+ }
105
+ await delay(currentInterval);
106
+ }
107
+ }
108
+ async function printNewLogs(builder, buildId, linesSeen) {
109
+ try {
110
+ const result = await builder.getBuildLogs(buildId);
111
+ if (!result.success || !result.data) {
112
+ return linesSeen;
113
+ }
114
+ const logs = Array.isArray(result.data.logs)
115
+ ? result.data.logs
116
+ : typeof result.data.logs === 'string'
117
+ ? result.data.logs.split('\n')
118
+ : [];
119
+ if (logs.length > linesSeen) {
120
+ const newLines = logs.slice(linesSeen);
121
+ for (const line of newLines) {
122
+ log.dim(` ${line}`);
123
+ }
124
+ return logs.length;
125
+ }
126
+ return linesSeen;
127
+ }
128
+ catch {
129
+ // Log fetching is best-effort, don't interrupt the watch
130
+ return linesSeen;
131
+ }
132
+ }
133
+ function delay(ms) {
134
+ return new Promise(resolve => setTimeout(resolve, ms));
135
+ }
136
+ //# sourceMappingURL=watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.js","sourceRoot":"","sources":["../../src/lib/watch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ # Next.js Configuration
2
+ NEXT_PUBLIC_APP_NAME={{name}}
3
+
4
+ # Bio-id OAuth2 Configuration
5
+ # Register an OAuth2 app at https://bio-id.tawa.insureco.io/admin/oauth
6
+ # Set redirect URI to: http://localhost:3000/api/auth/callback (dev)
7
+ BIO_ID_URL=https://bio-id.tawa.insureco.io
8
+ APP_URL=http://localhost:3000
9
+ OAUTH_CLIENT_ID=your-client-id
10
+ OAUTH_CLIENT_SECRET=your-client-secret
11
+ JWT_SECRET=your-jwt-secret
12
+
13
+ # API Configuration
14
+ # NEXT_PUBLIC_API_URL=https://api.example.com
@@ -0,0 +1,51 @@
1
+ FROM node:22-alpine AS base
2
+
3
+ # Install dependencies only when needed
4
+ FROM base AS deps
5
+ RUN apk add --no-cache libc6-compat
6
+ WORKDIR /app
7
+
8
+ COPY package*.json ./
9
+ RUN npm ci
10
+
11
+ # Build the application
12
+ FROM base AS builder
13
+ WORKDIR /app
14
+ COPY --from=deps /app/node_modules ./node_modules
15
+ COPY . .
16
+
17
+ ENV NEXT_TELEMETRY_DISABLED 1
18
+
19
+ RUN npm run build
20
+
21
+ # Production image
22
+ FROM base AS runner
23
+ WORKDIR /app
24
+
25
+ ENV NODE_ENV production
26
+ ENV NEXT_TELEMETRY_DISABLED 1
27
+
28
+ RUN addgroup --system --gid 1001 nodejs
29
+ RUN adduser --system --uid 1001 nextjs
30
+
31
+ COPY --from=builder /app/public ./public
32
+
33
+ # Set correct permissions for prerender cache
34
+ RUN mkdir .next
35
+ RUN chown nextjs:nodejs .next
36
+
37
+ # Automatically leverage output traces to reduce image size
38
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
39
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
40
+
41
+ USER nextjs
42
+
43
+ EXPOSE 3000
44
+
45
+ ENV PORT 3000
46
+ ENV HOSTNAME "0.0.0.0"
47
+
48
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
49
+ CMD wget --no-verbose --tries=1 --spider http://localhost:3000/api/health || exit 1
50
+
51
+ CMD ["node", "server.js"]
@@ -0,0 +1,128 @@
1
+ # {{name}}
2
+
3
+ {{description}}
4
+
5
+ ## Getting Started
6
+
7
+ ### Prerequisites
8
+
9
+ - Node.js 20+
10
+ - npm or pnpm
11
+ - A Bio-id OAuth2 application (see Setup below)
12
+
13
+ ### OAuth Setup
14
+
15
+ 1. Register an OAuth2 app at `https://bio-id.tawa.insureco.io/admin/oauth`
16
+ - Name: `{{name}}`
17
+ - Redirect URI: `http://localhost:3000/api/auth/callback`
18
+ - Copy the **Client ID** and **Client Secret**
19
+
20
+ 2. Create `.env` from the example:
21
+ ```bash
22
+ cp .env.example .env
23
+ ```
24
+
25
+ 3. Fill in the OAuth values in `.env`:
26
+ ```
27
+ OAUTH_CLIENT_ID=your-client-id
28
+ OAUTH_CLIENT_SECRET=your-client-secret
29
+ JWT_SECRET=your-jwt-secret
30
+ ```
31
+
32
+ ### Installation
33
+
34
+ ```bash
35
+ npm install
36
+ ```
37
+
38
+ ### Development
39
+
40
+ ```bash
41
+ npm run dev
42
+ ```
43
+
44
+ Open [http://localhost:3000](http://localhost:3000) in your browser.
45
+
46
+ ### Production Build
47
+
48
+ ```bash
49
+ npm run build
50
+ npm start
51
+ ```
52
+
53
+ ## Authentication Flow
54
+
55
+ This app uses Bio-id OAuth2 with PKCE for authentication:
56
+
57
+ 1. User clicks **Sign In** on the home page
58
+ 2. Redirected to Bio-id authorization page
59
+ 3. After consent, redirected back to `/api/auth/callback`
60
+ 4. Tokens stored in httpOnly cookies
61
+ 5. Protected routes check cookies via middleware + server components
62
+
63
+ ### Auth Routes
64
+
65
+ | Route | Method | Description |
66
+ |-------|--------|-------------|
67
+ | `/api/auth/login` | GET | Initiates OAuth flow (redirects to Bio-id) |
68
+ | `/api/auth/callback` | GET | Handles OAuth callback, sets cookies |
69
+ | `/api/auth/logout` | GET/POST | Clears auth cookies |
70
+ | `/api/auth/session` | GET | Returns current user as JSON |
71
+
72
+ ### Route Protection
73
+
74
+ - **Middleware** (`src/middleware.ts`): Redirects unauthenticated users to login for `/dashboard` routes
75
+ - **Server components**: Use `getCurrentUser()` from `@/lib/auth` for server-side auth checks
76
+
77
+ ## Project Structure
78
+
79
+ ```
80
+ {{name}}/
81
+ ├── src/
82
+ │ ├── lib/
83
+ │ │ └── auth.ts # OAuth2 library (PKCE, tokens, cookies)
84
+ │ ├── middleware.ts # Route protection middleware
85
+ │ └── app/
86
+ │ ├── layout.tsx # Root layout
87
+ │ ├── page.tsx # Home page (login/logout UI)
88
+ │ ├── dashboard/
89
+ │ │ └── page.tsx # Protected dashboard
90
+ │ └── api/
91
+ │ ├── health/ # Health check endpoint
92
+ │ │ └── route.ts
93
+ │ ├── example/ # Example API route
94
+ │ │ └── route.ts
95
+ │ └── auth/ # OAuth2 routes
96
+ │ ├── login/route.ts
97
+ │ ├── callback/route.ts
98
+ │ ├── logout/route.ts
99
+ │ └── session/route.ts
100
+ ├── public/
101
+ ├── helm/
102
+ │ └── {{name}}/ # Kubernetes Helm chart
103
+ ├── next.config.js
104
+ ├── Dockerfile
105
+ └── catalog-info.yaml # Backstage service catalog
106
+ ```
107
+
108
+ ## Deployment
109
+
110
+ ### Using iec-cli
111
+
112
+ ```bash
113
+ # Link repository for auto-deploy
114
+ iec link
115
+
116
+ # Manual deploy to sandbox
117
+ iec deploy
118
+
119
+ # Deploy to production
120
+ iec deploy --prod
121
+ ```
122
+
123
+ When deploying, set the OAuth environment variables in your Helm values or via `iec env set`.
124
+
125
+ ## Learn More
126
+
127
+ - [Next.js Documentation](https://nextjs.org/docs)
128
+ - [Tawa Platform Docs](https://docs.tawa.insureco.io)
@@ -0,0 +1,17 @@
1
+ apiVersion: backstage.io/v1alpha1
2
+ kind: Component
3
+ metadata:
4
+ name: {{name}}
5
+ description: {{description}}
6
+ tags:
7
+ - tawa
8
+ - frontend
9
+ - nextjs
10
+ annotations:
11
+ insureco.io/framework: nextjs
12
+ insureco.io/language: typescript
13
+ insureco.io/auth: bio-id
14
+ spec:
15
+ type: service
16
+ lifecycle: experimental
17
+ owner: team-platform
@@ -0,0 +1,9 @@
1
+ apiVersion: v2
2
+ name: {{name}}
3
+ description: Helm chart for {{name}} (Next.js)
4
+ type: application
5
+ version: 0.1.0
6
+ appVersion: "0.1.0"
7
+ maintainers:
8
+ - name: team-platform
9
+ email: platform@insureco.io