Skip to content

Commit 4ad1b75

Browse files
committed
Release 1.1
- add database-backed TSPL label templates across Go and Python - switch print, preview, calibration, and iOS bridge flows to template-rendered TSPL - reduce printer runtime config to host and port only - align Python with Go for template CRUD, export/import, and websocket print events - simplify the web printer settings UI around raw TSPL templates - make English the default web locale and clean up mixed-language strings - refresh magic-link emails and improve HTML mail encoding - update the changelog for the full combined 1.1 change set
1 parent 5637400 commit 4ad1b75

42 files changed

Lines changed: 3370 additions & 387 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Changelog
2+
3+
## 2026-04-30
4+
5+
### TSPL label templates and printing
6+
- Introduced database-backed TSPL label templates across the Go and Python backends.
7+
- Added label template API endpoints for metadata, listing, creating, updating, deleting, and setting defaults.
8+
- Switched item, location, preview/test print, calibration, and iOS bridge print jobs over to template-rendered TSPL.
9+
- Added default template seeding and automatic fallback default reassignment when deleting the active default template.
10+
- Included label templates in Python admin export/import so the print setup travels with the database state.
11+
- Normalized built-in templates to a single `QR only 20x20` system template and enforced printer-friendly TSPL termination before sending.
12+
13+
### Printer configuration and iOS bridge
14+
- Reduced global printer configuration to host and port only.
15+
- Removed the legacy runtime printer speed, density, label size, and gap settings from the Go, Python, web, and iOS runtime paths.
16+
- Aligned the local iOS printer bridge with the same built-in 20x20 TSPL style used by the server templates.
17+
- Added websocket-based print request/result handling to the Python backend to match the cross-device bridge flow.
18+
19+
### Web app and settings polish
20+
- Simplified the printer settings UI around TSPL templates and inline test printing.
21+
- Added placeholder and TSPL command reference sections to the settings UI.
22+
- Removed the failed preview-heavy direction and kept the template editor focused on raw TSPL authoring.
23+
- Set English as the default locale for the web app, with German as an explicit option.
24+
- Cleaned up mixed-language strings and tightened content widths for item details, categories, locations, and master data pages.
25+
26+
### Magic-link e-mails
27+
- Reworked the magic-link e-mail layout into a cleaner branded structure.
28+
- Switched the e-mail copy to English by default.
29+
- Added footer links and branding elements.
30+
- Added quoted-printable transfer encoding for more reliable HTML mail character handling.

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
APP_VERSION=1.0
1+
APP_VERSION=1.1
22
APP_BUILD=dev

backend/go/build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ WEBAPP_DIR="$BUILD_DIR/webapp"
99
DIST_DIR="$BUILD_DIR"
1010
GOCACHE_DIR="${GOCACHE:-$BUILD_DIR/.gocache}"
1111
DEFAULT_ENV_SOURCE="$ROOT_DIR/config/default.env"
12-
APP_VERSION="1.0"
12+
APP_VERSION="1.1"
1313
APP_BUILD="dev"
1414
SERVER_BASENAME="itemplus-server"
1515
SERVER_LOCAL_NAME="itemplus-server"

backend/go/internal/config/config.go

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,8 @@ type Config struct {
3535
UploadDir string
3636
MaxUploadSize int64
3737

38-
PrinterHost string
39-
PrinterPort int
40-
PrinterSpeed int
41-
PrinterDensity int
42-
PrinterLabelWidth int
43-
PrinterLabelHeight int
44-
PrinterGap float64
38+
PrinterHost string
39+
PrinterPort int
4540

4641
SMTPHost string
4742
SMTPPort int
@@ -89,13 +84,8 @@ func Load() {
8984
UploadDir: resolveAbsolutePath(envStr("UPLOAD_DIR", filepath.Join(dataDir, "uploads")), envBaseDir),
9085
MaxUploadSize: envInt64("MAX_UPLOAD_SIZE", 200*1024*1024), // 200 MB default
9186

92-
PrinterHost: envStr("PRINTER_HOST", ""),
93-
PrinterPort: envInt("PRINTER_PORT", 9100),
94-
PrinterSpeed: envInt("PRINTER_SPEED", 4),
95-
PrinterDensity: envInt("PRINTER_DENSITY", 8),
96-
PrinterLabelWidth: envInt("PRINTER_LABEL_WIDTH", 20),
97-
PrinterLabelHeight: envInt("PRINTER_LABEL_HEIGHT", 20),
98-
PrinterGap: envFloat("PRINTER_GAP", 3.0),
87+
PrinterHost: envStr("PRINTER_HOST", ""),
88+
PrinterPort: envInt("PRINTER_PORT", 9100),
9989

10090
SMTPHost: envStr("SMTP_HOST", ""),
10191
SMTPPort: envInt("SMTP_PORT", 465),

backend/go/internal/config/templates/default.env

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ─── item+ Default Configuration ───
2-
# Embedded fallback for first-start .env creation.
3-
# The editable source of truth lives in /config/default.env at the repo root.
2+
# This file is used as the shared first-start template for both backends.
3+
# item+ creates a local .env from this file when none exists yet.
44

55
# Remove the next line after reviewing your local .env.
66
ITEMPLUS_SETUP_REQUIRED=remove_this_line_after_review
@@ -41,11 +41,6 @@ MAX_UPLOAD_SIZE=157286400
4141
# ─── TSC Thermal Printer ───
4242
PRINTER_HOST=
4343
PRINTER_PORT=9100
44-
PRINTER_SPEED=4
45-
PRINTER_DENSITY=8
46-
PRINTER_LABEL_WIDTH=20
47-
PRINTER_LABEL_HEIGHT=20
48-
PRINTER_GAP=3.0
4944

5045
# ─── SMTP (Magic Link) ───
5146
SMTP_HOST=

backend/go/internal/database/database.go

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package database
22

33
import (
4+
"database/sql"
45
"log"
6+
"fmt"
57
"strings"
8+
"time"
69

710
_ "github.com/glebarez/go-sqlite"
811
"github.com/itemplus/backend/internal/config"
12+
"github.com/itemplus/backend/internal/services"
913
"github.com/jmoiron/sqlx"
1014
)
1115

@@ -61,6 +65,35 @@ func createTables() {
6165
`CREATE INDEX IF NOT EXISTS idx_users_apple_sub ON users(apple_sub)`,
6266
`CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)`,
6367

68+
// Label templates
69+
`CREATE TABLE IF NOT EXISTS label_templates (
70+
id INTEGER PRIMARY KEY AUTOINCREMENT,
71+
system_key TEXT UNIQUE,
72+
name TEXT NOT NULL,
73+
description TEXT,
74+
target TEXT NOT NULL,
75+
dpi INTEGER NOT NULL DEFAULT 600,
76+
width_mm INTEGER NOT NULL,
77+
height_mm INTEGER NOT NULL,
78+
gap_mm REAL NOT NULL DEFAULT 3,
79+
speed INTEGER NOT NULL DEFAULT 4,
80+
density INTEGER NOT NULL DEFAULT 8,
81+
direction INTEGER NOT NULL DEFAULT 1,
82+
reference_x INTEGER NOT NULL DEFAULT 0,
83+
reference_y INTEGER NOT NULL DEFAULT 0,
84+
shift_x INTEGER NOT NULL DEFAULT 0,
85+
shift_y INTEGER NOT NULL DEFAULT 0,
86+
copies_default INTEGER NOT NULL DEFAULT 1,
87+
is_default BOOLEAN DEFAULT 0,
88+
is_system BOOLEAN DEFAULT 0,
89+
is_active BOOLEAN DEFAULT 1,
90+
tspl_template TEXT NOT NULL,
91+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
92+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
93+
)`,
94+
`CREATE INDEX IF NOT EXISTS idx_label_templates_target ON label_templates(target)`,
95+
`CREATE INDEX IF NOT EXISTS idx_label_templates_active ON label_templates(is_active)`,
96+
6497
// Device Sessions
6598
`CREATE TABLE IF NOT EXISTS device_sessions (
6699
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -117,6 +150,69 @@ func createTables() {
117150
DB.MustExec(sql)
118151
}
119152

153+
if _, err := DB.Exec("ALTER TABLE label_templates ADD COLUMN dpi INTEGER NOT NULL DEFAULT 600"); err != nil && !strings.Contains(strings.ToLower(err.Error()), "duplicate column") {
154+
log.Fatalf("Could not migrate label_templates.dpi: %v", err)
155+
}
156+
157+
ensureDefaultLabelTemplates()
158+
}
159+
160+
func ensureDefaultLabelTemplates() {
161+
now := time.Now().UTC().Format(time.RFC3339)
162+
defaults := services.DefaultLabelTemplates()
163+
164+
if _, err := DB.Exec(
165+
`UPDATE label_templates
166+
SET tspl_template = REPLACE(tspl_template, 'PRINT {{copies}}', 'PRINT 1'),
167+
updated_at = ?
168+
WHERE is_system = 1 AND tspl_template LIKE '%PRINT {{copies}}%'`,
169+
now,
170+
); err != nil {
171+
log.Printf("Could not normalize legacy system label templates: %v", err)
172+
}
173+
174+
allowedKeys := make([]string, 0, len(defaults))
175+
for _, def := range defaults {
176+
allowedKeys = append(allowedKeys, def.SystemKey)
177+
}
178+
179+
if len(allowedKeys) > 0 {
180+
placeholders := strings.TrimSuffix(strings.Repeat("?,", len(allowedKeys)), ",")
181+
args := make([]interface{}, 0, len(allowedKeys))
182+
for _, key := range allowedKeys {
183+
args = append(args, key)
184+
}
185+
query := fmt.Sprintf("DELETE FROM label_templates WHERE is_system = 1 AND system_key NOT IN (%s)", placeholders)
186+
if _, err := DB.Exec(query, args...); err != nil {
187+
log.Printf("Could not cleanup legacy system label templates: %v", err)
188+
}
189+
}
190+
191+
for _, def := range defaults {
192+
var existingID int
193+
err := DB.Get(&existingID, "SELECT id FROM label_templates WHERE system_key = ?", def.SystemKey)
194+
if err == nil {
195+
continue
196+
}
197+
if err != sql.ErrNoRows {
198+
log.Printf("Could not query label template %s: %v", def.SystemKey, err)
199+
continue
200+
}
201+
202+
_, err = DB.Exec(
203+
`INSERT INTO label_templates (
204+
system_key, name, description, target, dpi, width_mm, height_mm, gap_mm, speed, density, direction,
205+
reference_x, reference_y, shift_x, shift_y, copies_default, is_default, is_system, is_active,
206+
tspl_template, created_at, updated_at
207+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, 1, ?, ?, ?)`,
208+
def.SystemKey, def.Name, def.Description, def.Target, def.DPI, def.WidthMM, def.HeightMM, def.GapMM, def.Speed, def.Density,
209+
def.Direction, def.ReferenceX, def.ReferenceY, def.ShiftX, def.ShiftY, def.CopiesDefault, def.IsDefault,
210+
def.TSPLTemplate, now, now,
211+
)
212+
if err != nil {
213+
log.Printf("Could not insert default label template %s: %v", def.SystemKey, err)
214+
}
215+
}
120216
}
121217

122218
func realmTables(p string) []string {

0 commit comments

Comments
 (0)