Conversation
Agent-Logs-Url: https://github.com/IoTSharp/Gateways/sessions/bf30490c-271b-47f0-8c48-ebb460f37aa9 Co-authored-by: maikebing <3445167+maikebing@users.noreply.github.com>
Agent-Logs-Url: https://github.com/IoTSharp/Gateways/sessions/bf30490c-271b-47f0-8c48-ebb460f37aa9 Co-authored-by: maikebing <3445167+maikebing@users.noreply.github.com>
Agent-Logs-Url: https://github.com/IoTSharp/Gateways/sessions/bf30490c-271b-47f0-8c48-ebb460f37aa9 Co-authored-by: maikebing <3445167+maikebing@users.noreply.github.com>
Agent-Logs-Url: https://github.com/IoTSharp/Gateways/sessions/bf30490c-271b-47f0-8c48-ebb460f37aa9 Co-authored-by: maikebing <3445167+maikebing@users.noreply.github.com>
Agent-Logs-Url: https://github.com/IoTSharp/Gateways/sessions/4a2f5b84-18da-45c7-b490-cbd9a8f92c5e Co-authored-by: maikebing <3445167+maikebing@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a unified driver-based gateway scaffold across backend (Domain/Application/Infrastructure), hosting (Web API + Worker), persistence (SQLite), upload transports (HTTP/MQTT), and a Vue 3 admin shell (src/gateway-web) to browse/configure and debug runtime read/write.
Changes:
- Added DDD-style backend projects (Domain/Application/Infrastructure) implementing unified driver contracts, a driver catalog, runtime orchestration, and SQLite persistence.
- Added ASP.NET Core minimal API + Worker host to expose configuration/runtime endpoints and run polling tasks.
- Added a Vue 3 + Vite frontend shell with navigation and resource panels for drivers/channels/devices/points/tasks/transforms/uploads/runtime.
Reviewed changes
Copilot reviewed 90 out of 149 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| src/gateway-web/vite.config.ts | Vite config with Vue plugin, devtools plugin, and @ alias. |
| src/gateway-web/tsconfig.node.json | Node-side TS config for tooling configs (vite/vitest/etc.). |
| src/gateway-web/tsconfig.json | TS project references for app + node configs. |
| src/gateway-web/tsconfig.app.json | App TS config extending Vue DOM defaults and path mapping. |
| src/gateway-web/src/views/UploadsView.vue | UI panel for upload channels and upload routes. |
| src/gateway-web/src/views/TransformsView.vue | UI panel for transform rules. |
| src/gateway-web/src/views/TasksView.vue | UI panel for polling tasks. |
| src/gateway-web/src/views/RuntimeView.vue | UI for debug read/write against runtime endpoints. |
| src/gateway-web/src/views/PointsView.vue | UI panel for points. |
| src/gateway-web/src/views/DriversView.vue | Drivers catalog view driven by Pinia store. |
| src/gateway-web/src/views/DevicesView.vue | UI panel for devices. |
| src/gateway-web/src/views/DashboardView.vue | Dashboard/overview landing page. |
| src/gateway-web/src/views/ChannelsView.vue | UI panel for channels. |
| src/gateway-web/src/stores/gateway.ts | Pinia store for loading driver catalog. |
| src/gateway-web/src/router/index.ts | Frontend routes for dashboard and configuration sections. |
| src/gateway-web/src/main.ts | Vue app bootstrap (Pinia + router). |
| src/gateway-web/src/components/ResourcePanel.vue | Reusable resource fetch/refresh + JSON rendering panel. |
| src/gateway-web/src/assets/main.css | Base styling for layout/panels/navigation. |
| src/gateway-web/src/api.ts | Fetch wrapper for JSON GET/POST against backend API. |
| src/gateway-web/src/App.vue | Main layout with sidebar navigation and router outlet. |
| src/gateway-web/public/favicon.ico | Frontend favicon asset. |
| src/gateway-web/package.json | Frontend dependencies and build/typecheck scripts. |
| src/gateway-web/index.html | Vite HTML entry. |
| src/gateway-web/env.d.ts | Vite client types reference. |
| src/gateway-web/README.md | Default Vue/Vite template README for the frontend project. |
| src/gateway-web/.vscode/extensions.json | Recommends Volar extension. |
| src/gateway-web/.gitignore | Frontend-specific ignores (dist/node_modules/etc.). |
| src/IoTSharp.Gateways.Worker/appsettings.json | Worker settings (SQLite connection, logging). |
| src/IoTSharp.Gateways.Worker/appsettings.Development.json | Worker dev logging config. |
| src/IoTSharp.Gateways.Worker/Properties/launchSettings.json | Worker launch profile. |
| src/IoTSharp.Gateways.Worker/Program.cs | Worker host setup + schema init + polling worker registration. |
| src/IoTSharp.Gateways.Worker/IoTSharp.Gateways.Worker.csproj | Worker project definition and references. |
| src/IoTSharp.Gateways.Worker/GatewayPollingWorker.cs | Background polling loop invoking runtime service per task. |
| src/IoTSharp.Gateways.Infrastructure/Uploads/GatewayUploads.cs | HTTP + MQTT upload transports and registry. |
| src/IoTSharp.Gateways.Infrastructure/Persistence/SqliteGatewayPersistence.cs | SQLite schema init + repository + connection factory. |
| src/IoTSharp.Gateways.Infrastructure/IoTSharp.Gateways.Infrastructure.csproj | Infrastructure packages and references (Dapper/Sqlite/MQTTnet/IoTClient). |
| src/IoTSharp.Gateways.Infrastructure/GatewayInfrastructureServiceCollection.cs | DI registration for drivers, repository, uploads, and infra services. |
| src/IoTSharp.Gateways.Infrastructure/Drivers/GatewayDrivers.cs | IoTClient-backed driver adapters + registry implementation. |
| src/IoTSharp.Gateways.Domain/IoTSharp.Gateways.Domain.csproj | Domain project definition. |
| src/IoTSharp.Gateways.Domain/GatewayModel.cs | Domain enums/models + unified driver & upload contracts. |
| src/IoTSharp.Gateways.Application/IoTSharp.Gateways.Application.csproj | Application project definition. |
| src/IoTSharp.Gateways.Application/GatewayApplication.cs | Repository contract + configuration/runtime/catalog/transformation services. |
| src/IoTSharp.Gateways.Api/appsettings.json | API settings (SQLite connection, logging, allowed hosts). |
| src/IoTSharp.Gateways.Api/appsettings.Development.json | API dev logging config. |
| src/IoTSharp.Gateways.Api/Properties/launchSettings.json | API launch profiles/urls. |
| src/IoTSharp.Gateways.Api/Program.cs | Minimal API endpoints for config + runtime read/write/poll + CORS. |
| src/IoTSharp.Gateways.Api/IoTSharp.Gateways.Api.http | HTTP scratch file for testing endpoints. |
| src/IoTSharp.Gateways.Api/IoTSharp.Gateways.Api.csproj | API project definition and references. |
| README.md | New root README describing layered architecture and run/build steps. |
| IoTSharp.Gateways.sln | Updated solution structure to include layered projects and worker/api. |
| .gitignore | Expanded ignores for bin/obj and frontend build artifacts. |
Files not reviewed (1)
- Data/Migrations/20221029113750_CreateIdentitySchema.Designer.cs: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| public sealed class SqliteGatewayConnectionFactory : IGatewayDbConnectionFactory | ||
| { | ||
| private readonly GatewayStorageOptions _options; | ||
|
|
||
| public SqliteGatewayConnectionFactory(IOptions<GatewayStorageOptions> options) | ||
| { | ||
| _options = options.Value; | ||
| Batteries_V2.Init(); | ||
| } |
There was a problem hiding this comment.
SqliteGatewayConnectionFactory calls SQLitePCL.Batteries_V2.Init() and imports SQLitePCL, but IoTSharp.Gateways.Infrastructure.csproj does not reference any SQLitePCLRaw/* package. This will fail to compile/restore. Either remove the SQLitePCL dependency (not needed when using Microsoft.Data.Sqlite) or add the appropriate SQLitePCLRaw bundle package.
| var client = _httpClientFactory.CreateClient(nameof(HttpUploadTransport)); | ||
| var settings = GatewayJson.Parse(channel.SettingsJson); | ||
| if (settings.TryGetValue("headerName", out var headerName) && !string.IsNullOrWhiteSpace(headerName) && settings.TryGetValue("headerValue", out var headerValue) && !string.IsNullOrWhiteSpace(headerValue)) | ||
| { | ||
| client.DefaultRequestHeaders.Remove(headerName); | ||
| client.DefaultRequestHeaders.Add(headerName, headerValue); | ||
| } | ||
|
|
||
| if (settings.TryGetValue("bearerToken", out var bearerToken) && !string.IsNullOrWhiteSpace(bearerToken)) | ||
| { | ||
| client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", bearerToken); | ||
| } |
There was a problem hiding this comment.
HttpUploadTransport mutates HttpClient.DefaultRequestHeaders per upload based on per-channel settings. Since HttpClient instances from IHttpClientFactory are pooled/reused, this can leak headers across different channels/requests and is not safe under concurrent uploads. Prefer setting headers on a per-request HttpRequestMessage (or create a dedicated named client per channel config without mutating shared defaults).
| "http": { | ||
| "commandName": "Project", | ||
| "dotnetRunMessages": true, | ||
| "launchBrowser": true, | ||
| "launchUrl": "weatherforecast", | ||
| "applicationUrl": "http://localhost:5268", | ||
| "environmentVariables": { | ||
| "ASPNETCORE_ENVIRONMENT": "Development" | ||
| } | ||
| }, | ||
| "https": { | ||
| "commandName": "Project", | ||
| "dotnetRunMessages": true, | ||
| "launchBrowser": true, | ||
| "launchUrl": "weatherforecast", | ||
| "applicationUrl": "https://localhost:7165;http://localhost:5268", | ||
| "environmentVariables": { |
There was a problem hiding this comment.
launchUrl is set to weatherforecast, but the API doesn't map a /weatherforecast endpoint in Program.cs (only /api/*). This will open a 404 on launch; update launchUrl to something valid (e.g., api/health) or remove it.
| - `/home/runner/work/Gateways/Gateways/src/IoTSharp.Gateways.Domain`:领域模型与统一驱动接口 | ||
| - `/home/runner/work/Gateways/Gateways/src/IoTSharp.Gateways.Application`:配置服务、运行编排、转换服务 | ||
| - `/home/runner/work/Gateways/Gateways/src/IoTSharp.Gateways.Infrastructure`:SQLite 仓储、驱动适配器、HTTP/MQTT 上传 | ||
| - `/home/runner/work/Gateways/Gateways/src/IoTSharp.Gateways.Api`:ASP.NET Core Web API | ||
| - `/home/runner/work/Gateways/Gateways/src/IoTSharp.Gateways.Worker`:后台采集执行 Worker | ||
| - `/home/runner/work/Gateways/Gateways/src/gateway-web`:Vue 3 管理前端 |
There was a problem hiding this comment.
The README hard-codes GitHub Actions runner absolute paths (e.g. /home/runner/work/Gateways/Gateways/...), which won't work for local checkouts. Use relative paths (e.g. src/IoTSharp.Gateways.Api/...) or repo-root based instructions instead.
| builder.Services.Configure<Microsoft.Extensions.Hosting.HostOptions>(options => | ||
| { | ||
| options.BackgroundServiceExceptionBehavior = BackgroundServiceExceptionBehavior.Ignore; | ||
| }); |
There was a problem hiding this comment.
BackgroundServiceExceptionBehavior is set to Ignore, which can leave the process running even when hosted services fail unexpectedly, making failures harder to detect operationally. Prefer StopHost (default) or make this configurable per environment if you need to tolerate certain failures.
| async function request<T>(path: string, init?: RequestInit): Promise<T> { | ||
| const response = await fetch(`${apiBaseUrl}${path}`, { | ||
| headers: { | ||
| 'Content-Type': 'application/json', | ||
| ...(init?.headers ?? {}), | ||
| }, | ||
| ...init, | ||
| }) |
There was a problem hiding this comment.
request() spreads ...init after building the headers object, so if a caller passes init.headers, it will overwrite the merged headers and drop the default Content-Type: application/json (and any other defaults). Reorder the spread so init is applied first, then build/merge headers last.
| export default defineConfig({ | ||
| plugins: [ | ||
| vue(), | ||
| vueDevTools(), | ||
| ], |
There was a problem hiding this comment.
vite-plugin-vue-devtools is currently enabled unconditionally, which can impact production builds (bundle size / devtools exposure). Consider enabling it only for command === 'serve' / non-production mode.
| builder.Services.AddCors(options => | ||
| { | ||
| options.AddDefaultPolicy(policy => policy.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin()); | ||
| }); |
There was a problem hiding this comment.
The API enables a default CORS policy with AllowAnyOrigin/AnyHeader/AnyMethod. If this host is exposed beyond local development, this is a security risk. Consider restricting origins via configuration (and/or only enabling permissive CORS in Development).
| @IoTSharp.Gateways.Api_HostAddress = http://localhost:5268 | ||
|
|
||
| GET {{IoTSharp.Gateways.Api_HostAddress}}/weatherforecast/ | ||
| Accept: application/json |
There was a problem hiding this comment.
The sample request targets /weatherforecast/, but this project currently exposes health/config endpoints under /api/* (e.g. /api/health). Updating this file will prevent confusion when testing the API.
| var tasks = (await repository.GetPollingTasksAsync(stoppingToken)).Where(task => task.Enabled).ToArray(); | ||
| var now = DateTimeOffset.UtcNow; | ||
| foreach (var task in tasks) | ||
| { | ||
| if (_nextRuns.TryGetValue(task.Id, out var nextRun) && nextRun > now) | ||
| { | ||
| continue; | ||
| } | ||
|
|
||
| var report = await runtimeService.ExecutePollingTaskAsync(task.Id, stoppingToken); | ||
| _logger.LogInformation("Executed polling task {TaskName} with {SuccessCount} successes and {FailureCount} failures.", report.TaskName, report.SuccessCount, report.FailureCount); | ||
| _nextRuns[task.Id] = now.AddSeconds(Math.Max(task.IntervalSeconds, 1)); | ||
| } | ||
| } | ||
| catch (Exception exception) | ||
| { | ||
| _logger.LogError(exception, "Gateway polling iteration failed."); | ||
| } | ||
|
|
||
| await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken); | ||
| } |
There was a problem hiding this comment.
The polling loop sleeps for a fixed 5 seconds, so tasks with IntervalSeconds < 5 cannot run at their configured cadence. Also, _nextRuns never removes entries for tasks that were deleted/disabled, so it can grow unbounded over time. Consider computing the next delay based on the soonest scheduled run and pruning _nextRuns for tasks not returned by the repository.
No description provided.