Skip to content

Commit 3804194

Browse files
[Feat] Environment loader (#69)
* feat(shared/environment): add shared environment library with services, providers, and interfaces * chore(scripts): add environment loading scripts and example .env file * refactor(ui): switch to shared EnvironmentService and remove legacy environment.ts * chore(config): update workspace and package config for new environment library * fix: base config spread object is not iterable. Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * fix: dynamic import of env files from a library is fragile; prefer injected/global env Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * refactor(environment): remove google config caching Remove the internal caching mechanism for Google configuration. The `IGoogleConfig` object is now computed dynamically on each access. This simplifies the `EnvironmentService` by removing mutable state and associated cache invalidation logic, as direct `getValue` calls are sufficiently performant. * refactor(core-environment): use specific types for validation errors and warnings Improve type safety and clarity by using `ValidationError` and `ValidationWarning` types for the validation results arrays instead of `any[]`. This enhances readability and maintainability of the environment validation logic. * feat(desktop): make application name configurable Allow setting the application name via the `APP_NAME` environment variable. If `APP_NAME` is not provided, the name defaults to 'Ever Rec Desktop'. This enables easier branding and custom naming for different build targets or white-labeling scenarios. Includes minor trailing comma formatting adjustments. * build(scripts): remove explicit env loading from package scripts The dedicated `prestart` and `prebuild` hooks for loading environment variables are no longer required. Environment variables are now implicitly managed by the Nx build system or handled by individual application processes. * chore(env): remove obsolete example env vars Removes `DATABASE_URL` and `JWT_SECRET` from `.env.example`. These variables are no longer part of the standard application setup or are handled differently, making their inclusion in the example file misleading or unnecessary. * refactor(environment): remove unused database and JWT environment config The `DATABASE_URL` and `JWT_SECRET` variables, along with their accessors, are no longer required in the shared environment configuration. This simplifies the environment interface by removing properties that are either unused or are now managed through more secure or direct injection methods. * chore(env): quote app name value in example Ensures the application name, which contains a space, is correctly parsed when loaded from the environment file. * refactor(environment): use inject function for CoreEnvironmentService Migrates the EnvironmentService to use the `inject` function from `@angular/core` instead of constructor-based dependency injection for CoreEnvironmentService. This aligns with modern Angular practices. --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
1 parent eb19c2b commit 3804194

30 files changed

Lines changed: 985 additions & 152 deletions

.env.example

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Example environment variables
2+
# Copy this file to .env and fill in your actual values
3+
4+
# API Configuration
5+
API_URL=http://localhost:3000
6+
7+
# App Configuration
8+
DEBUG=true
9+
PORT=4201
10+
APP_NAME="Ever Rec"
11+
APP_ICON=fiber_smart_record
12+
IS_PLUGIN=false
13+
CAN_USE_WEB_WORKER=true
14+
USE_EMULATORS=true
15+
16+
# Google OAuth Configuration
17+
GOOGLE_REDIRECT_URI=http://localhost:4200/authorize
18+
GOOGLE_CLIENT_ID=your-google-client-id

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ Thumbs.db
4646

4747
.nx
4848

49+
# Environment variables
50+
.env
51+
.env.local
52+
.env.*.local
53+
54+
# Generated environment files
55+
apps/ui/src/environment/environment.dev.ts
56+
apps/ui/src/environment/environment.prod.ts
4957

5058
#Ignore cursor AI rules
5159
.cursor/rules/codacy.mdc

apps/desktop/src/app/app.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default class App {
1010

1111
static main(app: Electron.App): void {
1212
App.application = app;
13-
App.application.setName('Ever Rec Desktop');
13+
App.application.setName(process.env['APP_NAME'] || 'Ever Rec Desktop');
1414

1515
App.application.on('ready', App.handleAppReady);
1616
App.application.on('activate', App.handleAppActivate);
@@ -49,7 +49,7 @@ export default class App {
4949
} else {
5050
callback(false); // Denied
5151
}
52-
}
52+
},
5353
);
5454
}
5555

@@ -63,7 +63,7 @@ export default class App {
6363
callback({ video: sources[0], audio: 'loopback' });
6464
});
6565
},
66-
{ useSystemPicker: true }
66+
{ useSystemPicker: true },
6767
);
6868
}
6969

apps/ui/project.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,27 @@
66
"sourceRoot": "apps/ui/src",
77
"tags": [],
88
"targets": {
9+
"load-env": {
10+
"executor": "nx:run-commands",
11+
"options": {
12+
"command": "node scripts/load-env.js",
13+
"cwd": "{workspaceRoot}"
14+
},
15+
"configurations": {
16+
"production": {
17+
"command": "node scripts/load-env-prod.js",
18+
"cwd": "{workspaceRoot}"
19+
}
20+
}
21+
},
922
"build": {
1023
"executor": "@angular-devkit/build-angular:application",
24+
"dependsOn": [
25+
{
26+
"target": "load-env",
27+
"params": "forward"
28+
}
29+
],
1130
"outputs": ["{options.outputPath}"],
1231
"options": {
1332
"outputPath": "dist/apps/ui",
@@ -60,6 +79,12 @@
6079
},
6180
"serve": {
6281
"executor": "@angular-devkit/build-angular:dev-server",
82+
"dependsOn": [
83+
{
84+
"target": "load-env",
85+
"params": "forward"
86+
}
87+
],
6388
"configurations": {
6489
"production": {
6590
"buildTarget": "ui:build:production"

apps/ui/src/app/app.config.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import { provideGenerateVideoDataAccess } from '@ever-co/generate-video-data-acc
2323
import { provideNotificationDataAccess } from '@ever-co/notification-data-access';
2424
import { providePhotoDataAccess } from '@ever-co/photo-data-access';
2525
import { provideScreenshotDataAccess } from '@ever-co/screenshot-data-access';
26-
import { REC_ENV } from '@ever-co/shared-service';
2726
import { provideSidebarDataAccess } from '@ever-co/sidebar-data-access';
2827
import { provideTimelineDataAccess } from '@ever-co/timeline-data-access';
2928
import { provideTimeLogDataAccess } from '@ever-co/timesheet-data-access';
@@ -35,12 +34,13 @@ import { provideWebcamDataAccess } from '@ever-co/webcam-data-access';
3534
import { provideEffects } from '@ngrx/effects';
3635
import { provideStore } from '@ngrx/store';
3736
import { provideStoreDevtools } from '@ngrx/store-devtools';
38-
import { Environment } from '../environment/environment';
37+
import { provideEnvironment } from '@ever-co/shared-environment';
3938
import { appRoutes } from './app.routes';
4039
import { provideCoreDataAccess } from '@ever-co/core-data-access';
4140

4241
export const appConfig: ApplicationConfig = {
4342
providers: [
43+
// Core providers
4444
provideScreenshotDataAccess(),
4545
provideBreadcrumbDataAccess(),
4646
provideVideoDataAccess(),
@@ -68,9 +68,6 @@ export const appConfig: ApplicationConfig = {
6868
provideUserDataAccess(),
6969
provideAuthDataAccess(),
7070
provideCoreDataAccess(),
71-
{
72-
provide: REC_ENV,
73-
useClass: Environment,
74-
},
71+
provideEnvironment(),
7572
],
7673
};

apps/ui/src/environment/environment.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

libs/shared/environment/README.md

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# @ever-co/shared-environment
2+
3+
A robust, SOLID-principles-based environment management library for Angular applications.
4+
5+
## Features
6+
7+
- 🏗️ **SOLID Principles** - Follows all five SOLID principles
8+
- 🎨 **Design Patterns** - Implements Singleton, Factory, Builder, Strategy, Adapter, Facade, Command, and Observer patterns
9+
- 🔒 **Type Safety** - Full TypeScript support with comprehensive interfaces
10+
-**Validation** - Extensible validation system with custom rules
11+
- 🔄 **Async/Sync** - Support for both asynchronous and synchronous operations
12+
- 🧪 **Testable** - Comprehensive unit tests included
13+
- 📦 **Reusable** - Can be used across multiple Angular applications
14+
15+
## Installation
16+
17+
```bash
18+
npm install @ever-co/shared-environment
19+
```
20+
21+
## Quick Start
22+
23+
```typescript
24+
import { Environment, EnvironmentService, createEnvironmentProviders } from '@ever-co/shared-environment';
25+
26+
// Configure environment loading
27+
Environment.setLoadFunction(() => {
28+
// Your environment loading logic
29+
return { API_URL: 'http://localhost:3000' };
30+
});
31+
32+
// Use in components
33+
@Component({
34+
providers: [...createEnvironmentProviders()]
35+
})
36+
export class MyComponent {
37+
constructor(private envService: EnvironmentService) {}
38+
39+
async ngOnInit() {
40+
const apiUrl = await this.envService.get('API_URL');
41+
console.log('API URL:', apiUrl);
42+
}
43+
}
44+
```
45+
46+
## Architecture
47+
48+
The library implements multiple design patterns:
49+
50+
- **Singleton Pattern** - Environment class ensures single instance
51+
- **Factory Pattern** - ConfigurationFactory creates configuration objects
52+
- **Builder Pattern** - ConfigurationBuilder constructs complex configurations
53+
- **Strategy Pattern** - Different loading strategies for various environments
54+
- **Adapter Pattern** - Adapts new system to legacy interfaces
55+
- **Facade Pattern** - EnvironmentService provides simplified interface
56+
57+
## API Reference
58+
59+
### Environment Class
60+
61+
```typescript
62+
// Get singleton instance
63+
const env = Environment.getInstance();
64+
65+
// Access configuration
66+
console.log(env.apiUrl);
67+
console.log(env.appName);
68+
console.log(env.debug);
69+
```
70+
71+
### EnvironmentService
72+
73+
```typescript
74+
// Async operations
75+
const apiUrl = await envService.get('API_URL', 'default');
76+
const isDebug = await envService.getBoolean('DEBUG', false);
77+
const port = await envService.getNumber('PORT', 3000);
78+
79+
// Validation
80+
const validation = await envService.getValidationResult();
81+
```
82+
83+
### Configuration Models
84+
85+
```typescript
86+
import { ConfigurationFactory } from '@ever-co/shared-environment';
87+
88+
const config = ConfigurationFactory.createConfiguration(envVars);
89+
console.log(config.apiUrl);
90+
console.log(config.google.clientId);
91+
```
92+
93+
## Testing
94+
95+
```bash
96+
# Run tests
97+
nx test environment
98+
99+
# Build library
100+
nx build environment
101+
```
102+
103+
## Contributing
104+
105+
1. Follow SOLID principles
106+
2. Add comprehensive tests
107+
3. Update documentation
108+
4. Ensure backward compatibility
109+
110+
## License
111+
112+
AGPL-3.0
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
const nx = require('@nx/eslint-plugin');
2+
const baseConfig = require('../../../.eslintrc.json');
3+
4+
module.exports = [
5+
...(Array.isArray(baseConfig) ? baseConfig : [baseConfig]),
6+
{
7+
files: ['**/*.json'],
8+
rules: {
9+
'@nx/dependency-checks': [
10+
'error',
11+
{
12+
ignoredFiles: ['{projectRoot}/eslint.config.{js,cjs,mjs,ts,cts,mts}'],
13+
},
14+
],
15+
},
16+
languageOptions: {
17+
parser: require('jsonc-eslint-parser'),
18+
},
19+
},
20+
...nx.configs['flat/angular'],
21+
...nx.configs['flat/angular-template'],
22+
{
23+
files: ['**/*.ts'],
24+
rules: {
25+
'@angular-eslint/directive-selector': [
26+
'error',
27+
{
28+
type: 'attribute',
29+
prefix: 'lib',
30+
style: 'camelCase',
31+
},
32+
],
33+
'@angular-eslint/component-selector': [
34+
'error',
35+
{
36+
type: 'element',
37+
prefix: 'lib',
38+
style: 'kebab-case',
39+
},
40+
],
41+
},
42+
},
43+
{
44+
files: ['**/*.html'],
45+
// Override or add rules here
46+
rules: {},
47+
},
48+
];
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
export default {
2+
displayName: 'environment',
3+
preset: '../../../jest.preset.js',
4+
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
5+
coverageDirectory: '../../../coverage/libs/shared/environment',
6+
transform: {
7+
'^.+\\.(ts|mjs|js|html)$': [
8+
'jest-preset-angular',
9+
{
10+
tsconfig: '<rootDir>/tsconfig.spec.json',
11+
stringifyContentPathRegex: '\\.(html|svg)$',
12+
},
13+
],
14+
},
15+
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
16+
snapshotSerializers: [
17+
'jest-preset-angular/build/serializers/no-ng-attributes',
18+
'jest-preset-angular/build/serializers/ng-snapshot',
19+
'jest-preset-angular/build/serializers/html-comment',
20+
],
21+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"$schema": "../../../node_modules/ng-packagr/ng-package.schema.json",
3+
"dest": "../../../dist/libs/shared/environment",
4+
"lib": {
5+
"entryFile": "src/index.ts"
6+
}
7+
}

0 commit comments

Comments
 (0)