@tari-project/tarijs 0.10.1 → 0.12.0
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.
- package/README.md +127 -67
- package/TODO.md +91 -3
- package/docusaurus/tari-docs/README.md +200 -17
- package/docusaurus/tari-docs/docs/api-reference.md +665 -0
- package/docusaurus/tari-docs/docs/contributing.md +619 -0
- package/docusaurus/tari-docs/docs/guides/getting-started-tutorial.md +965 -0
- package/docusaurus/tari-docs/docs/guides/production-deployment.md +977 -0
- package/docusaurus/tari-docs/docs/index.md +114 -11
- package/docusaurus/tari-docs/docs/installation.md +142 -1
- package/docusaurus/tari-docs/docs/signers/metamask.md +529 -0
- package/docusaurus/tari-docs/docs/troubleshooting.md +661 -0
- package/docusaurus/tari-docs/package.json +4 -4
- package/eslint.config.mjs +9 -0
- package/examples/vite-typescript-react/README.md +9 -0
- package/examples/vite-typescript-react/eslint.config.js +23 -0
- package/examples/vite-typescript-react/index.html +13 -0
- package/examples/vite-typescript-react/package.json +35 -0
- package/examples/vite-typescript-react/public/vite.svg +1 -0
- package/examples/vite-typescript-react/src/App.css +42 -0
- package/examples/vite-typescript-react/src/App.tsx +50 -0
- package/examples/vite-typescript-react/src/assets/react.svg +1 -0
- package/examples/vite-typescript-react/src/index.css +68 -0
- package/examples/vite-typescript-react/src/main.tsx +10 -0
- package/examples/vite-typescript-react/src/vite-env.d.ts +1 -0
- package/examples/vite-typescript-react/tsconfig.app.json +27 -0
- package/examples/vite-typescript-react/tsconfig.json +7 -0
- package/examples/vite-typescript-react/tsconfig.node.json +25 -0
- package/examples/vite-typescript-react/vite.config.ts +7 -0
- package/package.json +16 -3
- package/packages/builders/package.json +2 -2
- package/packages/builders/src/helpers/submitTransaction.ts +10 -35
- package/packages/builders/src/transaction/TransactionBuilder.ts +227 -29
- package/packages/indexer_provider/package.json +2 -2
- package/packages/indexer_provider/src/provider.ts +5 -5
- package/packages/indexer_provider/tsconfig.json +4 -2
- package/packages/metamask_signer/package.json +2 -2
- package/packages/metamask_signer/src/index.ts +3 -15
- package/packages/{tari_permissions → permissions}/package.json +2 -2
- package/packages/{tari_permissions → permissions}/src/helpers.ts +1 -1
- package/packages/permissions/src/index.ts +2 -0
- package/packages/{tari_permissions/src/tari_permissions.ts → permissions/src/permissions.ts} +56 -6
- package/packages/react-mui-connect-button/moon.yml +71 -0
- package/packages/react-mui-connect-button/package.json +40 -0
- package/packages/react-mui-connect-button/src/Logos.tsx +60 -0
- package/packages/react-mui-connect-button/src/TariConnectButton.tsx +51 -0
- package/packages/react-mui-connect-button/src/TariWalletSelectionDialog.tsx +116 -0
- package/packages/react-mui-connect-button/src/content/tari-logo-white.svg +18 -0
- package/packages/react-mui-connect-button/src/content/tari-logo.svg +18 -0
- package/packages/react-mui-connect-button/src/content/walletconnect-logo.svg +13 -0
- package/packages/react-mui-connect-button/src/defaultPermissions.ts +0 -0
- package/packages/react-mui-connect-button/src/index.ts +24 -0
- package/packages/react-mui-connect-button/tsconfig.json +31 -0
- package/packages/tari_provider/package.json +2 -2
- package/packages/tari_provider/src/TariProvider.ts +6 -1
- package/packages/tari_signer/package.json +2 -2
- package/packages/tari_universe/package.json +2 -2
- package/packages/tari_universe/tsconfig.json +4 -2
- package/packages/tarijs/package.json +2 -2
- package/packages/tarijs/src/index.ts +27 -49
- package/packages/tarijs/src/templates/Account.ts +7 -4
- package/packages/tarijs/test/integration-tests/.env +1 -1
- package/packages/tarijs/test/integration-tests/wallet_daemon/json_rpc_provider.spec.ts +112 -73
- package/packages/tarijs/tsconfig.json +6 -4
- package/packages/tarijs/vitest.config.ts +2 -1
- package/packages/tarijs_types/package.json +4 -3
- package/packages/tarijs_types/src/Account.ts +68 -0
- package/packages/tarijs_types/src/Amount.ts +5 -1
- package/packages/tarijs_types/src/TransactionResult.ts +1 -8
- package/packages/tarijs_types/src/consts.ts +3 -0
- package/packages/tarijs_types/src/helpers/index.ts +4 -0
- package/packages/tarijs_types/src/helpers/simpleResult.ts +345 -0
- package/packages/tarijs_types/src/helpers/txResult.ts +1 -2
- package/packages/tarijs_types/src/index.ts +8 -0
- package/packages/tarijs_types/src/signer.ts +1 -1
- package/packages/wallet_daemon/package.json +2 -2
- package/packages/wallet_daemon/src/provider.ts +8 -6
- package/packages/wallet_daemon/src/signer.ts +18 -8
- package/packages/wallet_daemon/tsconfig.json +1 -1
- package/packages/walletconnect/package.json +3 -2
- package/packages/walletconnect/src/index.ts +54 -28
- package/packages/walletconnect/tsconfig.json +3 -0
- package/pnpm-workspace.yaml +15 -7
- package/scripts/check_versions.sh +4 -0
- package/scripts/clean_everything.sh +38 -0
- package/tsconfig.json +6 -0
- package/typedoc.json +10 -0
- package/packages/tari_permissions/src/index.ts +0 -2
- /package/packages/{tari_permissions → permissions}/moon.yml +0 -0
- /package/packages/{tari_permissions → permissions}/tsconfig.json +0 -0
|
@@ -0,0 +1,977 @@
|
|
|
1
|
+
---
|
|
2
|
+
sidebar_position: 3
|
|
3
|
+
title: Production Deployment
|
|
4
|
+
description: Deploy tari.js applications to production with confidence - security, performance, and monitoring best practices
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Production Deployment Guide 🚀
|
|
8
|
+
|
|
9
|
+
> **Ship with confidence** — Comprehensive guide to deploying tari.js applications in production environments with enterprise-grade security, performance, and reliability.
|
|
10
|
+
|
|
11
|
+
This guide covers everything you need to deploy tari.js applications safely and efficiently in production, from security hardening to performance optimization and monitoring.
|
|
12
|
+
|
|
13
|
+
## 🎯 **Pre-Deployment Checklist**
|
|
14
|
+
|
|
15
|
+
### **Security Audit**
|
|
16
|
+
- [ ] **No hardcoded secrets** — All sensitive data in environment variables
|
|
17
|
+
- [ ] **HTTPS enforced** — SSL/TLS certificates properly configured
|
|
18
|
+
- [ ] **Input validation** — All user inputs properly sanitized
|
|
19
|
+
- [ ] **Error handling** — No sensitive information in error messages
|
|
20
|
+
- [ ] **Dependency audit** — All packages scanned for vulnerabilities
|
|
21
|
+
- [ ] **Wallet permissions** — Minimal required permissions only
|
|
22
|
+
|
|
23
|
+
### **Performance Optimization**
|
|
24
|
+
- [ ] **Bundle analysis** — Bundle size optimized and analyzed
|
|
25
|
+
- [ ] **Code splitting** — Dynamic imports for wallet modules
|
|
26
|
+
- [ ] **Asset optimization** — Images and assets compressed
|
|
27
|
+
- [ ] **Caching strategy** — Proper cache headers configured
|
|
28
|
+
- [ ] **CDN setup** — Static assets served via CDN
|
|
29
|
+
- [ ] **Connection pooling** — Efficient wallet connection management
|
|
30
|
+
|
|
31
|
+
### **Testing & Quality**
|
|
32
|
+
- [ ] **Test coverage >90%** — Comprehensive test suite
|
|
33
|
+
- [ ] **E2E tests passing** — End-to-end user workflows tested
|
|
34
|
+
- [ ] **Load testing** — Performance under expected traffic
|
|
35
|
+
- [ ] **Security testing** — Penetration testing completed
|
|
36
|
+
- [ ] **Cross-browser testing** — Compatibility verified
|
|
37
|
+
- [ ] **Mobile testing** — Responsive design validated
|
|
38
|
+
|
|
39
|
+
## 🔒 **Security Best Practices**
|
|
40
|
+
|
|
41
|
+
### **Environment Configuration**
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Production environment variables
|
|
45
|
+
NODE_ENV=production
|
|
46
|
+
TARI_NETWORK=mainnet
|
|
47
|
+
TARI_INDEXER_URL=https://your-secure-indexer.example.com
|
|
48
|
+
TARI_WALLET_DAEMON_URL=wss://your-secure-daemon.example.com
|
|
49
|
+
|
|
50
|
+
# Security headers
|
|
51
|
+
FORCE_HTTPS=true
|
|
52
|
+
HSTS_MAX_AGE=31536000
|
|
53
|
+
CSP_ENABLED=true
|
|
54
|
+
CORS_ORIGIN=https://yourdomain.com
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### **Content Security Policy (CSP)**
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<!-- Strict CSP for production -->
|
|
61
|
+
<meta http-equiv="Content-Security-Policy"
|
|
62
|
+
content="
|
|
63
|
+
default-src 'self';
|
|
64
|
+
script-src 'self' 'unsafe-eval' https://cdnjs.cloudflare.com;
|
|
65
|
+
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
|
|
66
|
+
font-src 'self' https://fonts.gstatic.com;
|
|
67
|
+
connect-src 'self' wss: https: ws:;
|
|
68
|
+
img-src 'self' data: https:;
|
|
69
|
+
frame-src 'none';
|
|
70
|
+
object-src 'none';
|
|
71
|
+
base-uri 'self';
|
|
72
|
+
form-action 'self';
|
|
73
|
+
">
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### **Secure Wallet Configuration**
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
// Production wallet configuration
|
|
80
|
+
const secureWalletConfig = {
|
|
81
|
+
// Network configuration
|
|
82
|
+
network: 'mainnet',
|
|
83
|
+
|
|
84
|
+
// Connection security
|
|
85
|
+
enforceHttps: true,
|
|
86
|
+
validateCertificates: true,
|
|
87
|
+
connectionTimeout: 30000,
|
|
88
|
+
|
|
89
|
+
// Authentication
|
|
90
|
+
requirePermissions: true,
|
|
91
|
+
permissionWhitelist: [
|
|
92
|
+
'accounts:read',
|
|
93
|
+
'transactions:submit',
|
|
94
|
+
'substates:read'
|
|
95
|
+
],
|
|
96
|
+
|
|
97
|
+
// Error handling
|
|
98
|
+
sanitizeErrors: true,
|
|
99
|
+
hideStackTraces: true,
|
|
100
|
+
|
|
101
|
+
// Rate limiting
|
|
102
|
+
maxRequestsPerMinute: 60,
|
|
103
|
+
burstLimit: 10,
|
|
104
|
+
|
|
105
|
+
// Monitoring
|
|
106
|
+
enableAuditLog: true,
|
|
107
|
+
logLevel: 'info'
|
|
108
|
+
};
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### **API Security**
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// Secure API wrapper with rate limiting
|
|
115
|
+
export class SecureWalletAPI {
|
|
116
|
+
private rateLimiter: RateLimiter;
|
|
117
|
+
private auditLogger: AuditLogger;
|
|
118
|
+
|
|
119
|
+
constructor(config: SecureWalletConfig) {
|
|
120
|
+
this.rateLimiter = new RateLimiter({
|
|
121
|
+
max: config.maxRequestsPerMinute,
|
|
122
|
+
windowMs: 60000
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
this.auditLogger = new AuditLogger({
|
|
126
|
+
level: config.logLevel,
|
|
127
|
+
destination: config.auditLogPath
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async submitTransaction(transaction: Transaction): Promise<TransactionResult> {
|
|
132
|
+
// Rate limiting
|
|
133
|
+
await this.rateLimiter.check();
|
|
134
|
+
|
|
135
|
+
// Input validation
|
|
136
|
+
this.validateTransaction(transaction);
|
|
137
|
+
|
|
138
|
+
// Audit logging
|
|
139
|
+
this.auditLogger.log('transaction_submit', {
|
|
140
|
+
transactionId: transaction.id,
|
|
141
|
+
userId: this.getCurrentUserId(),
|
|
142
|
+
timestamp: new Date().toISOString()
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
return await this.signer.submitTransaction({ transaction });
|
|
147
|
+
} catch (error) {
|
|
148
|
+
// Sanitize error for client
|
|
149
|
+
throw this.sanitizeError(error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
private validateTransaction(transaction: Transaction): void {
|
|
154
|
+
if (!transaction || !transaction.instructions) {
|
|
155
|
+
throw new ValidationError('Invalid transaction structure');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Additional validation logic
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
private sanitizeError(error: Error): Error {
|
|
162
|
+
// Remove sensitive information from error messages
|
|
163
|
+
return new Error('Transaction failed. Please try again.');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## ⚡ **Performance Optimization**
|
|
169
|
+
|
|
170
|
+
### **Bundle Optimization**
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
// webpack.config.js for production
|
|
174
|
+
const path = require('path');
|
|
175
|
+
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
|
|
176
|
+
|
|
177
|
+
module.exports = {
|
|
178
|
+
mode: 'production',
|
|
179
|
+
|
|
180
|
+
optimization: {
|
|
181
|
+
splitChunks: {
|
|
182
|
+
chunks: 'all',
|
|
183
|
+
cacheGroups: {
|
|
184
|
+
// Separate tari.js chunks for better caching
|
|
185
|
+
tari: {
|
|
186
|
+
test: /[\\/]node_modules[\\/]@tari-project[\\/]/,
|
|
187
|
+
name: 'tari',
|
|
188
|
+
chunks: 'all',
|
|
189
|
+
priority: 10
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
// Wallet-specific chunks
|
|
193
|
+
wallets: {
|
|
194
|
+
test: /[\\/]node_modules[\\/]@tari-project[\\/](wallet-daemon|metamask-signer|tari-universe)[\\/]/,
|
|
195
|
+
name: 'wallets',
|
|
196
|
+
chunks: 'async',
|
|
197
|
+
priority: 20
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
// Tree shaking
|
|
203
|
+
usedExports: true,
|
|
204
|
+
sideEffects: false
|
|
205
|
+
},
|
|
206
|
+
|
|
207
|
+
plugins: [
|
|
208
|
+
// Analyze bundle size
|
|
209
|
+
new BundleAnalyzerPlugin({
|
|
210
|
+
analyzerMode: 'static',
|
|
211
|
+
generateStatsFile: true
|
|
212
|
+
})
|
|
213
|
+
]
|
|
214
|
+
};
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### **Dynamic Wallet Loading**
|
|
218
|
+
|
|
219
|
+
```typescript
|
|
220
|
+
// Lazy load wallet modules for better performance
|
|
221
|
+
export class WalletManager {
|
|
222
|
+
private walletCache = new Map<string, any>();
|
|
223
|
+
|
|
224
|
+
async loadWallet(type: WalletType): Promise<TariSigner> {
|
|
225
|
+
if (this.walletCache.has(type)) {
|
|
226
|
+
return this.walletCache.get(type);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
let WalletClass;
|
|
230
|
+
|
|
231
|
+
switch (type) {
|
|
232
|
+
case 'metamask':
|
|
233
|
+
const { MetaMaskSigner } = await import('@tari-project/metamask-signer');
|
|
234
|
+
WalletClass = MetaMaskSigner;
|
|
235
|
+
break;
|
|
236
|
+
|
|
237
|
+
case 'wallet-daemon':
|
|
238
|
+
const { WalletDaemonSigner } = await import('@tari-project/wallet-daemon');
|
|
239
|
+
WalletClass = WalletDaemonSigner;
|
|
240
|
+
break;
|
|
241
|
+
|
|
242
|
+
case 'tari-universe':
|
|
243
|
+
const { TariUniverseSigner } = await import('@tari-project/tari-universe');
|
|
244
|
+
WalletClass = TariUniverseSigner;
|
|
245
|
+
break;
|
|
246
|
+
|
|
247
|
+
default:
|
|
248
|
+
throw new Error(`Unsupported wallet type: ${type}`);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const wallet = new WalletClass(this.getWalletConfig(type));
|
|
252
|
+
this.walletCache.set(type, wallet);
|
|
253
|
+
|
|
254
|
+
return wallet;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### **Connection Pooling**
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
// Efficient connection management
|
|
263
|
+
export class ConnectionPool {
|
|
264
|
+
private connections = new Map<string, Connection>();
|
|
265
|
+
private maxConnections = 10;
|
|
266
|
+
private connectionTimeout = 30000;
|
|
267
|
+
|
|
268
|
+
async getConnection(endpoint: string): Promise<Connection> {
|
|
269
|
+
const existing = this.connections.get(endpoint);
|
|
270
|
+
|
|
271
|
+
if (existing && existing.isHealthy()) {
|
|
272
|
+
return existing;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (this.connections.size >= this.maxConnections) {
|
|
276
|
+
await this.cleanupStaleConnections();
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const connection = await this.createConnection(endpoint);
|
|
280
|
+
this.connections.set(endpoint, connection);
|
|
281
|
+
|
|
282
|
+
return connection;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
private async cleanupStaleConnections(): Promise<void> {
|
|
286
|
+
for (const [endpoint, connection] of this.connections) {
|
|
287
|
+
if (!connection.isHealthy()) {
|
|
288
|
+
await connection.close();
|
|
289
|
+
this.connections.delete(endpoint);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## 🏗️ **Infrastructure Setup**
|
|
297
|
+
|
|
298
|
+
### **Docker Production Build**
|
|
299
|
+
|
|
300
|
+
```dockerfile
|
|
301
|
+
# Multi-stage production build
|
|
302
|
+
FROM node:18-alpine AS builder
|
|
303
|
+
|
|
304
|
+
WORKDIR /app
|
|
305
|
+
|
|
306
|
+
# Copy package files
|
|
307
|
+
COPY package*.json ./
|
|
308
|
+
COPY pnpm-lock.yaml ./
|
|
309
|
+
|
|
310
|
+
# Install dependencies
|
|
311
|
+
RUN npm install -g pnpm
|
|
312
|
+
RUN pnpm install --frozen-lockfile
|
|
313
|
+
|
|
314
|
+
# Copy source
|
|
315
|
+
COPY . .
|
|
316
|
+
|
|
317
|
+
# Build application
|
|
318
|
+
RUN pnpm run build
|
|
319
|
+
|
|
320
|
+
# Production stage
|
|
321
|
+
FROM nginx:alpine AS production
|
|
322
|
+
|
|
323
|
+
# Copy built assets
|
|
324
|
+
COPY --from=builder /app/dist /usr/share/nginx/html
|
|
325
|
+
|
|
326
|
+
# Copy nginx configuration
|
|
327
|
+
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
328
|
+
|
|
329
|
+
# Security headers
|
|
330
|
+
RUN echo 'add_header X-Frame-Options "SAMEORIGIN" always;' >> /etc/nginx/conf.d/security.conf
|
|
331
|
+
RUN echo 'add_header X-Content-Type-Options "nosniff" always;' >> /etc/nginx/conf.d/security.conf
|
|
332
|
+
RUN echo 'add_header Referrer-Policy "strict-origin-when-cross-origin" always;' >> /etc/nginx/conf.d/security.conf
|
|
333
|
+
|
|
334
|
+
EXPOSE 80
|
|
335
|
+
|
|
336
|
+
CMD ["nginx", "-g", "daemon off;"]
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### **Nginx Configuration**
|
|
340
|
+
|
|
341
|
+
```nginx
|
|
342
|
+
# nginx.conf for production
|
|
343
|
+
server {
|
|
344
|
+
listen 80;
|
|
345
|
+
listen [::]:80;
|
|
346
|
+
server_name yourdomain.com;
|
|
347
|
+
|
|
348
|
+
# Redirect HTTP to HTTPS
|
|
349
|
+
return 301 https://$server_name$request_uri;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
server {
|
|
353
|
+
listen 443 ssl http2;
|
|
354
|
+
listen [::]:443 ssl http2;
|
|
355
|
+
server_name yourdomain.com;
|
|
356
|
+
|
|
357
|
+
# SSL configuration
|
|
358
|
+
ssl_certificate /path/to/certificate.crt;
|
|
359
|
+
ssl_certificate_key /path/to/private.key;
|
|
360
|
+
ssl_protocols TLSv1.2 TLSv1.3;
|
|
361
|
+
ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
|
|
362
|
+
ssl_prefer_server_ciphers off;
|
|
363
|
+
|
|
364
|
+
# Security headers
|
|
365
|
+
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
|
|
366
|
+
add_header X-Frame-Options "SAMEORIGIN" always;
|
|
367
|
+
add_header X-Content-Type-Options "nosniff" always;
|
|
368
|
+
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
|
369
|
+
|
|
370
|
+
# Serve static files
|
|
371
|
+
root /usr/share/nginx/html;
|
|
372
|
+
index index.html;
|
|
373
|
+
|
|
374
|
+
# Gzip compression
|
|
375
|
+
gzip on;
|
|
376
|
+
gzip_vary on;
|
|
377
|
+
gzip_min_length 1024;
|
|
378
|
+
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
|
|
379
|
+
|
|
380
|
+
# Cache static assets
|
|
381
|
+
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2)$ {
|
|
382
|
+
expires 1y;
|
|
383
|
+
add_header Cache-Control "public, immutable";
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
# API proxy (if needed)
|
|
387
|
+
location /api/ {
|
|
388
|
+
proxy_pass https://your-backend-api.com;
|
|
389
|
+
proxy_set_header Host $host;
|
|
390
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
391
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
392
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
# WebSocket proxy for wallet daemon
|
|
396
|
+
location /ws/ {
|
|
397
|
+
proxy_pass wss://your-wallet-daemon.com;
|
|
398
|
+
proxy_http_version 1.1;
|
|
399
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
400
|
+
proxy_set_header Connection "upgrade";
|
|
401
|
+
proxy_set_header Host $host;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
# SPA fallback
|
|
405
|
+
try_files $uri $uri/ /index.html;
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### **Kubernetes Deployment**
|
|
410
|
+
|
|
411
|
+
```yaml
|
|
412
|
+
# kubernetes-deployment.yaml
|
|
413
|
+
apiVersion: apps/v1
|
|
414
|
+
kind: Deployment
|
|
415
|
+
metadata:
|
|
416
|
+
name: tari-js-app
|
|
417
|
+
labels:
|
|
418
|
+
app: tari-js-app
|
|
419
|
+
spec:
|
|
420
|
+
replicas: 3
|
|
421
|
+
selector:
|
|
422
|
+
matchLabels:
|
|
423
|
+
app: tari-js-app
|
|
424
|
+
template:
|
|
425
|
+
metadata:
|
|
426
|
+
labels:
|
|
427
|
+
app: tari-js-app
|
|
428
|
+
spec:
|
|
429
|
+
containers:
|
|
430
|
+
- name: tari-js-app
|
|
431
|
+
image: your-registry/tari-js-app:latest
|
|
432
|
+
ports:
|
|
433
|
+
- containerPort: 80
|
|
434
|
+
env:
|
|
435
|
+
- name: NODE_ENV
|
|
436
|
+
value: "production"
|
|
437
|
+
- name: TARI_NETWORK
|
|
438
|
+
value: "mainnet"
|
|
439
|
+
resources:
|
|
440
|
+
requests:
|
|
441
|
+
memory: "256Mi"
|
|
442
|
+
cpu: "250m"
|
|
443
|
+
limits:
|
|
444
|
+
memory: "512Mi"
|
|
445
|
+
cpu: "500m"
|
|
446
|
+
livenessProbe:
|
|
447
|
+
httpGet:
|
|
448
|
+
path: /health
|
|
449
|
+
port: 80
|
|
450
|
+
initialDelaySeconds: 30
|
|
451
|
+
periodSeconds: 10
|
|
452
|
+
readinessProbe:
|
|
453
|
+
httpGet:
|
|
454
|
+
path: /ready
|
|
455
|
+
port: 80
|
|
456
|
+
initialDelaySeconds: 5
|
|
457
|
+
periodSeconds: 5
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
apiVersion: v1
|
|
461
|
+
kind: Service
|
|
462
|
+
metadata:
|
|
463
|
+
name: tari-js-service
|
|
464
|
+
spec:
|
|
465
|
+
selector:
|
|
466
|
+
app: tari-js-app
|
|
467
|
+
ports:
|
|
468
|
+
- protocol: TCP
|
|
469
|
+
port: 80
|
|
470
|
+
targetPort: 80
|
|
471
|
+
type: LoadBalancer
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
apiVersion: networking.k8s.io/v1
|
|
475
|
+
kind: Ingress
|
|
476
|
+
metadata:
|
|
477
|
+
name: tari-js-ingress
|
|
478
|
+
annotations:
|
|
479
|
+
kubernetes.io/ingress.class: nginx
|
|
480
|
+
cert-manager.io/cluster-issuer: letsencrypt-prod
|
|
481
|
+
nginx.ingress.kubernetes.io/ssl-redirect: "true"
|
|
482
|
+
spec:
|
|
483
|
+
tls:
|
|
484
|
+
- hosts:
|
|
485
|
+
- yourdomain.com
|
|
486
|
+
secretName: tari-js-tls
|
|
487
|
+
rules:
|
|
488
|
+
- host: yourdomain.com
|
|
489
|
+
http:
|
|
490
|
+
paths:
|
|
491
|
+
- path: /
|
|
492
|
+
pathType: Prefix
|
|
493
|
+
backend:
|
|
494
|
+
service:
|
|
495
|
+
name: tari-js-service
|
|
496
|
+
port:
|
|
497
|
+
number: 80
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
## 📊 **Monitoring & Observability**
|
|
501
|
+
|
|
502
|
+
### **Health Checks**
|
|
503
|
+
|
|
504
|
+
```typescript
|
|
505
|
+
// Comprehensive health check system
|
|
506
|
+
export class HealthChecker {
|
|
507
|
+
async checkHealth(): Promise<HealthStatus> {
|
|
508
|
+
const checks = await Promise.allSettled([
|
|
509
|
+
this.checkWalletConnectivity(),
|
|
510
|
+
this.checkIndexerConnectivity(),
|
|
511
|
+
this.checkDatabaseHealth(),
|
|
512
|
+
this.checkMemoryUsage(),
|
|
513
|
+
this.checkDiskSpace()
|
|
514
|
+
]);
|
|
515
|
+
|
|
516
|
+
return {
|
|
517
|
+
status: checks.every(c => c.status === 'fulfilled') ? 'healthy' : 'unhealthy',
|
|
518
|
+
timestamp: new Date().toISOString(),
|
|
519
|
+
checks: {
|
|
520
|
+
wallet: checks[0].status === 'fulfilled',
|
|
521
|
+
indexer: checks[1].status === 'fulfilled',
|
|
522
|
+
database: checks[2].status === 'fulfilled',
|
|
523
|
+
memory: checks[3].status === 'fulfilled',
|
|
524
|
+
disk: checks[4].status === 'fulfilled'
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
private async checkWalletConnectivity(): Promise<boolean> {
|
|
530
|
+
try {
|
|
531
|
+
const signer = new WalletDaemonSigner(this.config.wallet);
|
|
532
|
+
return signer.isConnected();
|
|
533
|
+
} catch {
|
|
534
|
+
return false;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
private async checkIndexerConnectivity(): Promise<boolean> {
|
|
539
|
+
try {
|
|
540
|
+
const provider = new IndexerProvider(this.config.indexer);
|
|
541
|
+
await provider.getNetworkInfo();
|
|
542
|
+
return true;
|
|
543
|
+
} catch {
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### **Metrics Collection**
|
|
551
|
+
|
|
552
|
+
```typescript
|
|
553
|
+
// Prometheus metrics
|
|
554
|
+
import { register, Counter, Histogram, Gauge } from 'prom-client';
|
|
555
|
+
|
|
556
|
+
export class MetricsCollector {
|
|
557
|
+
private transactionCounter = new Counter({
|
|
558
|
+
name: 'tari_transactions_total',
|
|
559
|
+
help: 'Total number of transactions processed',
|
|
560
|
+
labelNames: ['wallet_type', 'status']
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
private transactionDuration = new Histogram({
|
|
564
|
+
name: 'tari_transaction_duration_seconds',
|
|
565
|
+
help: 'Transaction processing duration',
|
|
566
|
+
buckets: [0.1, 0.5, 1, 2, 5, 10]
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
private walletConnections = new Gauge({
|
|
570
|
+
name: 'tari_wallet_connections_active',
|
|
571
|
+
help: 'Number of active wallet connections'
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
recordTransaction(walletType: string, status: string, duration: number): void {
|
|
575
|
+
this.transactionCounter.inc({ wallet_type: walletType, status });
|
|
576
|
+
this.transactionDuration.observe(duration);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
updateConnectionCount(count: number): void {
|
|
580
|
+
this.walletConnections.set(count);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
getMetrics(): string {
|
|
584
|
+
return register.metrics();
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### **Error Tracking**
|
|
590
|
+
|
|
591
|
+
```typescript
|
|
592
|
+
// Sentry error tracking
|
|
593
|
+
import * as Sentry from '@sentry/node';
|
|
594
|
+
|
|
595
|
+
export class ErrorTracker {
|
|
596
|
+
constructor() {
|
|
597
|
+
Sentry.init({
|
|
598
|
+
dsn: process.env.SENTRY_DSN,
|
|
599
|
+
environment: process.env.NODE_ENV,
|
|
600
|
+
beforeSend: this.sanitizeErrorData
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
captureException(error: Error, context?: any): void {
|
|
605
|
+
Sentry.withScope(scope => {
|
|
606
|
+
if (context) {
|
|
607
|
+
scope.setContext('additional', context);
|
|
608
|
+
}
|
|
609
|
+
scope.setTag('component', 'tari-wallet');
|
|
610
|
+
Sentry.captureException(error);
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
private sanitizeErrorData(event: Sentry.Event): Sentry.Event {
|
|
615
|
+
// Remove sensitive information from error reports
|
|
616
|
+
if (event.exception?.values) {
|
|
617
|
+
event.exception.values.forEach(exception => {
|
|
618
|
+
if (exception.stacktrace?.frames) {
|
|
619
|
+
exception.stacktrace.frames.forEach(frame => {
|
|
620
|
+
// Remove local file paths
|
|
621
|
+
if (frame.filename?.includes('/home/')) {
|
|
622
|
+
frame.filename = frame.filename.replace(/\/home\/[^\/]+/, '/home/user');
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return event;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
## 🔍 **Logging Strategy**
|
|
635
|
+
|
|
636
|
+
### **Structured Logging**
|
|
637
|
+
|
|
638
|
+
```typescript
|
|
639
|
+
// Winston structured logging
|
|
640
|
+
import winston from 'winston';
|
|
641
|
+
|
|
642
|
+
export class Logger {
|
|
643
|
+
private logger: winston.Logger;
|
|
644
|
+
|
|
645
|
+
constructor() {
|
|
646
|
+
this.logger = winston.createLogger({
|
|
647
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
648
|
+
format: winston.format.combine(
|
|
649
|
+
winston.format.timestamp(),
|
|
650
|
+
winston.format.errors({ stack: true }),
|
|
651
|
+
winston.format.json()
|
|
652
|
+
),
|
|
653
|
+
defaultMeta: {
|
|
654
|
+
service: 'tari-wallet-app',
|
|
655
|
+
version: process.env.APP_VERSION
|
|
656
|
+
},
|
|
657
|
+
transports: [
|
|
658
|
+
new winston.transports.File({
|
|
659
|
+
filename: 'logs/error.log',
|
|
660
|
+
level: 'error'
|
|
661
|
+
}),
|
|
662
|
+
new winston.transports.File({
|
|
663
|
+
filename: 'logs/combined.log'
|
|
664
|
+
})
|
|
665
|
+
]
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
if (process.env.NODE_ENV !== 'production') {
|
|
669
|
+
this.logger.add(new winston.transports.Console({
|
|
670
|
+
format: winston.format.simple()
|
|
671
|
+
}));
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
logTransaction(transactionId: string, walletType: string, userId?: string): void {
|
|
676
|
+
this.logger.info('Transaction processed', {
|
|
677
|
+
transactionId,
|
|
678
|
+
walletType,
|
|
679
|
+
userId: userId ? this.hashUserId(userId) : 'anonymous',
|
|
680
|
+
timestamp: new Date().toISOString()
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
logError(error: Error, context?: any): void {
|
|
685
|
+
this.logger.error('Application error', {
|
|
686
|
+
error: error.message,
|
|
687
|
+
stack: error.stack,
|
|
688
|
+
context: this.sanitizeContext(context)
|
|
689
|
+
});
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
private hashUserId(userId: string): string {
|
|
693
|
+
// Hash user ID for privacy
|
|
694
|
+
return require('crypto').createHash('sha256').update(userId).digest('hex').slice(0, 8);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
private sanitizeContext(context: any): any {
|
|
698
|
+
// Remove sensitive fields from log context
|
|
699
|
+
if (!context) return context;
|
|
700
|
+
|
|
701
|
+
const sanitized = { ...context };
|
|
702
|
+
delete sanitized.privateKey;
|
|
703
|
+
delete sanitized.password;
|
|
704
|
+
delete sanitized.token;
|
|
705
|
+
|
|
706
|
+
return sanitized;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
## 🚨 **Incident Response**
|
|
712
|
+
|
|
713
|
+
### **Alerting Configuration**
|
|
714
|
+
|
|
715
|
+
```yaml
|
|
716
|
+
# Prometheus alerting rules
|
|
717
|
+
groups:
|
|
718
|
+
- name: tari-wallet-alerts
|
|
719
|
+
rules:
|
|
720
|
+
- alert: HighErrorRate
|
|
721
|
+
expr: rate(tari_transactions_total{status="error"}[5m]) > 0.1
|
|
722
|
+
for: 2m
|
|
723
|
+
labels:
|
|
724
|
+
severity: warning
|
|
725
|
+
annotations:
|
|
726
|
+
summary: "High transaction error rate detected"
|
|
727
|
+
description: "Error rate is {{ $value }} per second"
|
|
728
|
+
|
|
729
|
+
- alert: WalletConnectionDown
|
|
730
|
+
expr: tari_wallet_connections_active == 0
|
|
731
|
+
for: 1m
|
|
732
|
+
labels:
|
|
733
|
+
severity: critical
|
|
734
|
+
annotations:
|
|
735
|
+
summary: "No active wallet connections"
|
|
736
|
+
description: "All wallet connections are down"
|
|
737
|
+
|
|
738
|
+
- alert: HighMemoryUsage
|
|
739
|
+
expr: process_resident_memory_bytes > 500000000
|
|
740
|
+
for: 5m
|
|
741
|
+
labels:
|
|
742
|
+
severity: warning
|
|
743
|
+
annotations:
|
|
744
|
+
summary: "High memory usage detected"
|
|
745
|
+
description: "Memory usage is {{ $value | humanize }}B"
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
### **Automated Recovery**
|
|
749
|
+
|
|
750
|
+
```typescript
|
|
751
|
+
// Circuit breaker for wallet connections
|
|
752
|
+
export class CircuitBreaker {
|
|
753
|
+
private failures = 0;
|
|
754
|
+
private lastFailTime = 0;
|
|
755
|
+
private state: 'closed' | 'open' | 'half-open' = 'closed';
|
|
756
|
+
|
|
757
|
+
constructor(
|
|
758
|
+
private threshold = 5,
|
|
759
|
+
private timeout = 60000 // 1 minute
|
|
760
|
+
) {}
|
|
761
|
+
|
|
762
|
+
async execute<T>(operation: () => Promise<T>): Promise<T> {
|
|
763
|
+
if (this.state === 'open') {
|
|
764
|
+
if (Date.now() - this.lastFailTime > this.timeout) {
|
|
765
|
+
this.state = 'half-open';
|
|
766
|
+
} else {
|
|
767
|
+
throw new Error('Circuit breaker is open');
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
try {
|
|
772
|
+
const result = await operation();
|
|
773
|
+
this.onSuccess();
|
|
774
|
+
return result;
|
|
775
|
+
} catch (error) {
|
|
776
|
+
this.onFailure();
|
|
777
|
+
throw error;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
private onSuccess(): void {
|
|
782
|
+
this.failures = 0;
|
|
783
|
+
this.state = 'closed';
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
private onFailure(): void {
|
|
787
|
+
this.failures++;
|
|
788
|
+
this.lastFailTime = Date.now();
|
|
789
|
+
|
|
790
|
+
if (this.failures >= this.threshold) {
|
|
791
|
+
this.state = 'open';
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
## 📈 **Performance Monitoring**
|
|
798
|
+
|
|
799
|
+
### **Real User Monitoring (RUM)**
|
|
800
|
+
|
|
801
|
+
```typescript
|
|
802
|
+
// Performance tracking
|
|
803
|
+
export class PerformanceMonitor {
|
|
804
|
+
trackWalletConnection(walletType: string): void {
|
|
805
|
+
const startTime = performance.now();
|
|
806
|
+
|
|
807
|
+
return {
|
|
808
|
+
complete: () => {
|
|
809
|
+
const duration = performance.now() - startTime;
|
|
810
|
+
this.sendMetric('wallet_connection_time', duration, {
|
|
811
|
+
wallet_type: walletType
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
trackTransactionSubmission(transactionSize: number): void {
|
|
818
|
+
const startTime = performance.now();
|
|
819
|
+
|
|
820
|
+
return {
|
|
821
|
+
complete: (success: boolean) => {
|
|
822
|
+
const duration = performance.now() - startTime;
|
|
823
|
+
this.sendMetric('transaction_submission_time', duration, {
|
|
824
|
+
transaction_size: transactionSize.toString(),
|
|
825
|
+
success: success.toString()
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
private sendMetric(name: string, value: number, tags: Record<string, string>): void {
|
|
832
|
+
// Send to your analytics service
|
|
833
|
+
if (typeof gtag !== 'undefined') {
|
|
834
|
+
gtag('event', name, {
|
|
835
|
+
custom_parameter: value,
|
|
836
|
+
...tags
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
## 🔄 **Continuous Deployment**
|
|
844
|
+
|
|
845
|
+
### **GitHub Actions Workflow**
|
|
846
|
+
|
|
847
|
+
```yaml
|
|
848
|
+
# .github/workflows/deploy-production.yml
|
|
849
|
+
name: Deploy to Production
|
|
850
|
+
|
|
851
|
+
on:
|
|
852
|
+
push:
|
|
853
|
+
branches: [main]
|
|
854
|
+
workflow_dispatch:
|
|
855
|
+
|
|
856
|
+
jobs:
|
|
857
|
+
test:
|
|
858
|
+
runs-on: ubuntu-latest
|
|
859
|
+
steps:
|
|
860
|
+
- uses: actions/checkout@v4
|
|
861
|
+
|
|
862
|
+
- name: Setup Node.js
|
|
863
|
+
uses: actions/setup-node@v4
|
|
864
|
+
with:
|
|
865
|
+
node-version: '18'
|
|
866
|
+
cache: 'pnpm'
|
|
867
|
+
|
|
868
|
+
- name: Install dependencies
|
|
869
|
+
run: pnpm install --frozen-lockfile
|
|
870
|
+
|
|
871
|
+
- name: Run tests
|
|
872
|
+
run: pnpm test
|
|
873
|
+
|
|
874
|
+
- name: Run security audit
|
|
875
|
+
run: pnpm audit
|
|
876
|
+
|
|
877
|
+
- name: Build application
|
|
878
|
+
run: pnpm build
|
|
879
|
+
|
|
880
|
+
- name: Run E2E tests
|
|
881
|
+
run: pnpm test:e2e
|
|
882
|
+
|
|
883
|
+
security-scan:
|
|
884
|
+
runs-on: ubuntu-latest
|
|
885
|
+
steps:
|
|
886
|
+
- uses: actions/checkout@v4
|
|
887
|
+
|
|
888
|
+
- name: Run Snyk security scan
|
|
889
|
+
uses: snyk/actions/node@master
|
|
890
|
+
env:
|
|
891
|
+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
|
892
|
+
|
|
893
|
+
deploy:
|
|
894
|
+
needs: [test, security-scan]
|
|
895
|
+
runs-on: ubuntu-latest
|
|
896
|
+
environment: production
|
|
897
|
+
|
|
898
|
+
steps:
|
|
899
|
+
- uses: actions/checkout@v4
|
|
900
|
+
|
|
901
|
+
- name: Setup Node.js
|
|
902
|
+
uses: actions/setup-node@v4
|
|
903
|
+
with:
|
|
904
|
+
node-version: '18'
|
|
905
|
+
cache: 'pnpm'
|
|
906
|
+
|
|
907
|
+
- name: Install dependencies
|
|
908
|
+
run: pnpm install --frozen-lockfile
|
|
909
|
+
|
|
910
|
+
- name: Build for production
|
|
911
|
+
run: pnpm build
|
|
912
|
+
env:
|
|
913
|
+
NODE_ENV: production
|
|
914
|
+
TARI_NETWORK: mainnet
|
|
915
|
+
|
|
916
|
+
- name: Build Docker image
|
|
917
|
+
run: |
|
|
918
|
+
docker build -t ${{ secrets.DOCKER_REGISTRY }}/tari-js-app:${{ github.sha }} .
|
|
919
|
+
docker tag ${{ secrets.DOCKER_REGISTRY }}/tari-js-app:${{ github.sha }} ${{ secrets.DOCKER_REGISTRY }}/tari-js-app:latest
|
|
920
|
+
|
|
921
|
+
- name: Push to registry
|
|
922
|
+
run: |
|
|
923
|
+
echo ${{ secrets.DOCKER_PASSWORD }} | docker login ${{ secrets.DOCKER_REGISTRY }} -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
|
924
|
+
docker push ${{ secrets.DOCKER_REGISTRY }}/tari-js-app:${{ github.sha }}
|
|
925
|
+
docker push ${{ secrets.DOCKER_REGISTRY }}/tari-js-app:latest
|
|
926
|
+
|
|
927
|
+
- name: Deploy to Kubernetes
|
|
928
|
+
run: |
|
|
929
|
+
kubectl set image deployment/tari-js-app tari-js-app=${{ secrets.DOCKER_REGISTRY }}/tari-js-app:${{ github.sha }}
|
|
930
|
+
kubectl rollout status deployment/tari-js-app
|
|
931
|
+
|
|
932
|
+
- name: Verify deployment
|
|
933
|
+
run: |
|
|
934
|
+
kubectl get pods -l app=tari-js-app
|
|
935
|
+
curl -f https://yourdomain.com/health
|
|
936
|
+
```
|
|
937
|
+
|
|
938
|
+
## 🎯 **Production Checklist**
|
|
939
|
+
|
|
940
|
+
### **Final Pre-Launch Verification**
|
|
941
|
+
|
|
942
|
+
- [ ] **Load testing completed** — Application handles expected traffic
|
|
943
|
+
- [ ] **Security scan passed** — No critical vulnerabilities
|
|
944
|
+
- [ ] **Monitoring configured** — Alerts and dashboards ready
|
|
945
|
+
- [ ] **Backup strategy** — Data backup and recovery procedures
|
|
946
|
+
- [ ] **Rollback plan** — Procedures for quick rollback if needed
|
|
947
|
+
- [ ] **Documentation updated** — Runbooks and operational guides
|
|
948
|
+
- [ ] **Team training** — Operations team familiar with deployment
|
|
949
|
+
- [ ] **Compliance check** — Legal and regulatory requirements met
|
|
950
|
+
|
|
951
|
+
### **Go-Live Protocol**
|
|
952
|
+
|
|
953
|
+
1. **Deploy during low-traffic period**
|
|
954
|
+
2. **Monitor metrics closely for first 24 hours**
|
|
955
|
+
3. **Verify all critical user journeys**
|
|
956
|
+
4. **Check error rates and performance**
|
|
957
|
+
5. **Confirm monitoring and alerting**
|
|
958
|
+
6. **Document any issues and resolutions**
|
|
959
|
+
|
|
960
|
+
## 🔗 **Related Resources**
|
|
961
|
+
|
|
962
|
+
- **[Installation Guide](../installation)** — Development environment setup
|
|
963
|
+
- **[Security Best Practices](https://github.com/tari-project/tari.js/blob/main/SECURITY.md)** — Comprehensive security checklist
|
|
964
|
+
- **[Performance Optimization](../troubleshooting#performance-issues)** — Advanced optimization techniques
|
|
965
|
+
- **[Monitoring Guide](../troubleshooting)** — Complete observability setup
|
|
966
|
+
- **[Troubleshooting](../troubleshooting)** — Common production issues
|
|
967
|
+
|
|
968
|
+
## 💬 **Support**
|
|
969
|
+
|
|
970
|
+
- **🚨 [Emergency Support](mailto:emergency@tari.com)** — Critical production issues
|
|
971
|
+
- **💬 [Discord](https://discord.gg/tari)** — Community support and discussion
|
|
972
|
+
- **📋 [Issue Tracker](https://github.com/tari-project/tari.js/issues)** — Bug reports and feature requests
|
|
973
|
+
- **📚 [Documentation](https://tari-project.github.io/tari.js/)** — Complete guides and references
|
|
974
|
+
|
|
975
|
+
---
|
|
976
|
+
|
|
977
|
+
**Ready for production?** Follow this guide step-by-step to deploy your tari.js application with enterprise-grade reliability and security! 🚀
|