ework-web 0.2.1 → 0.3.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.
@@ -0,0 +1,178 @@
1
+ -- ework schema (MySQL 8.0+ / MariaDB 10.5+). Applied idempotently on boot:
2
+ -- CREATE TABLE IF NOT EXISTS + CREATE INDEX (no IF NOT EXISTS — MySQL lacks it;
3
+ -- re-runs tolerate ER_DUP_KEYNAME 1061). FK constraint names are {{tokenized}}
4
+ -- so prefixed instances don't collide on constraint-name uniqueness. Date
5
+ -- columns are VARCHAR(40) holding ISO-8601 strings — the app formats dates in
6
+ -- JS, never SQL date arithmetic, so strings avoid Date-vs-string friction
7
+ -- across drivers. FK columns carry their own indexes (MySQL requirement). All
8
+ -- tables InnoDB + utf8mb4 for FK CASCADE + full Unicode (emoji).
9
+
10
+ CREATE TABLE IF NOT EXISTS {{users}} (
11
+ login VARCHAR(255) PRIMARY KEY,
12
+ kind VARCHAR(16) NOT NULL DEFAULT 'human'
13
+ CHECK (kind IN ('human','bot','system')),
14
+ display_name VARCHAR(255) DEFAULT NULL,
15
+ password_hash VARCHAR(255) DEFAULT NULL,
16
+ email VARCHAR(255) DEFAULT NULL,
17
+ is_admin TINYINT NOT NULL DEFAULT 0,
18
+ is_active TINYINT NOT NULL DEFAULT 1,
19
+ created_at VARCHAR(40) NOT NULL,
20
+ updated_at VARCHAR(40) NOT NULL DEFAULT ''
21
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
22
+
23
+ CREATE TABLE IF NOT EXISTS {{projects}} (
24
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
25
+ owner VARCHAR(255) NOT NULL,
26
+ name VARCHAR(255) NOT NULL,
27
+ description VARCHAR(2048) NOT NULL DEFAULT '',
28
+ upstream_urls VARCHAR(4096) NOT NULL DEFAULT '[]',
29
+ model VARCHAR(128) NOT NULL DEFAULT '',
30
+ created_at VARCHAR(40) NOT NULL,
31
+ updated_at VARCHAR(40) NOT NULL,
32
+ UNIQUE (owner, name)
33
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
34
+
35
+ CREATE TABLE IF NOT EXISTS {{model_cache}} (
36
+ provider_model VARCHAR(128) PRIMARY KEY,
37
+ label VARCHAR(255) NOT NULL,
38
+ refreshed_at VARCHAR(40) NOT NULL
39
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
40
+
41
+ CREATE TABLE IF NOT EXISTS {{issues}} (
42
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
43
+ project_id BIGINT NOT NULL,
44
+ number INT NOT NULL,
45
+ title VARCHAR(512) NOT NULL,
46
+ body TEXT NOT NULL,
47
+ state VARCHAR(16) NOT NULL DEFAULT 'open'
48
+ CHECK (state IN ('open','closed')),
49
+ author VARCHAR(255) NOT NULL,
50
+ created_at VARCHAR(40) NOT NULL,
51
+ updated_at VARCHAR(40) NOT NULL,
52
+ closed_at VARCHAR(40) DEFAULT NULL,
53
+ UNIQUE (project_id, number),
54
+ CONSTRAINT {{fk_issues_project}} FOREIGN KEY (project_id) REFERENCES {{projects}}(id) ON DELETE CASCADE,
55
+ CONSTRAINT {{fk_issues_author}} FOREIGN KEY (author) REFERENCES {{users}}(login)
56
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
57
+ CREATE INDEX issues_project_state_updated
58
+ ON {{issues}} (project_id, state, updated_at DESC);
59
+ CREATE INDEX issues_state_updated
60
+ ON {{issues}} (state, updated_at DESC);
61
+ CREATE INDEX issues_author ON {{issues}} (author);
62
+
63
+ CREATE TABLE IF NOT EXISTS {{comments}} (
64
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
65
+ issue_id BIGINT NOT NULL,
66
+ author VARCHAR(255) NOT NULL,
67
+ body TEXT NOT NULL,
68
+ created_at VARCHAR(40) NOT NULL,
69
+ updated_at VARCHAR(40) NOT NULL DEFAULT '',
70
+ CONSTRAINT {{fk_comments_issue}} FOREIGN KEY (issue_id) REFERENCES {{issues}}(id) ON DELETE CASCADE,
71
+ CONSTRAINT {{fk_comments_author}} FOREIGN KEY (author) REFERENCES {{users}}(login)
72
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
73
+ CREATE INDEX comments_issue_created ON {{comments}} (issue_id, created_at);
74
+ CREATE INDEX comments_author ON {{comments}} (author);
75
+
76
+ CREATE TABLE IF NOT EXISTS {{labels}} (
77
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
78
+ project_id BIGINT NOT NULL,
79
+ name VARCHAR(255) NOT NULL,
80
+ color VARCHAR(16) NOT NULL DEFAULT '#888888',
81
+ UNIQUE (project_id, name),
82
+ CONSTRAINT {{fk_labels_project}} FOREIGN KEY (project_id) REFERENCES {{projects}}(id) ON DELETE CASCADE
83
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
84
+
85
+ CREATE TABLE IF NOT EXISTS {{issue_labels}} (
86
+ issue_id BIGINT NOT NULL,
87
+ label_id BIGINT NOT NULL,
88
+ PRIMARY KEY (issue_id, label_id),
89
+ CONSTRAINT {{fk_il_issue}} FOREIGN KEY (issue_id) REFERENCES {{issues}}(id) ON DELETE CASCADE,
90
+ CONSTRAINT {{fk_il_label}} FOREIGN KEY (label_id) REFERENCES {{labels}}(id) ON DELETE CASCADE
91
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
92
+ CREATE INDEX issue_labels_label ON {{issue_labels}} (label_id);
93
+
94
+ CREATE TABLE IF NOT EXISTS {{reactions}} (
95
+ comment_id BIGINT NOT NULL,
96
+ user_login VARCHAR(255) NOT NULL,
97
+ content VARCHAR(64) NOT NULL,
98
+ PRIMARY KEY (comment_id, user_login, content),
99
+ CONSTRAINT {{fk_reactions_comment}} FOREIGN KEY (comment_id) REFERENCES {{comments}}(id) ON DELETE CASCADE,
100
+ CONSTRAINT {{fk_reactions_user}} FOREIGN KEY (user_login) REFERENCES {{users}}(login)
101
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
102
+ CREATE INDEX reactions_comment ON {{reactions}} (comment_id);
103
+ CREATE INDEX reactions_user ON {{reactions}} (user_login);
104
+
105
+ CREATE TABLE IF NOT EXISTS {{attachments}} (
106
+ uuid VARCHAR(64) PRIMARY KEY,
107
+ issue_id BIGINT NOT NULL,
108
+ filename VARCHAR(255) NOT NULL,
109
+ content_type VARCHAR(128) NOT NULL DEFAULT 'application/octet-stream',
110
+ size BIGINT NOT NULL,
111
+ blob_path VARCHAR(1024) NOT NULL,
112
+ uploaded_by VARCHAR(255) NOT NULL,
113
+ created_at VARCHAR(40) NOT NULL,
114
+ CONSTRAINT {{fk_attachments_issue}} FOREIGN KEY (issue_id) REFERENCES {{issues}}(id) ON DELETE CASCADE,
115
+ CONSTRAINT {{fk_attachments_user}} FOREIGN KEY (uploaded_by) REFERENCES {{users}}(login)
116
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
117
+ CREATE INDEX attachments_issue ON {{attachments}} (issue_id);
118
+ CREATE INDEX attachments_user ON {{attachments}} (uploaded_by);
119
+
120
+ CREATE TABLE IF NOT EXISTS {{webhooks}} (
121
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
122
+ project_id BIGINT NOT NULL,
123
+ url VARCHAR(1024) NOT NULL,
124
+ secret VARCHAR(255) NOT NULL DEFAULT '',
125
+ content_type VARCHAR(128) NOT NULL DEFAULT 'application/json',
126
+ events VARCHAR(2048) NOT NULL DEFAULT '["issues","issue_comment"]',
127
+ active TINYINT NOT NULL DEFAULT 1,
128
+ created_at VARCHAR(40) NOT NULL,
129
+ updated_at VARCHAR(40) NOT NULL,
130
+ CONSTRAINT {{fk_webhooks_project}} FOREIGN KEY (project_id) REFERENCES {{projects}}(id) ON DELETE CASCADE
131
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
132
+ CREATE INDEX webhooks_project ON {{webhooks}} (project_id);
133
+
134
+ CREATE TABLE IF NOT EXISTS {{webhook_deliveries}} (
135
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
136
+ webhook_id BIGINT NOT NULL,
137
+ event VARCHAR(64) NOT NULL,
138
+ delivery_uuid VARCHAR(64) NOT NULL,
139
+ payload LONGTEXT NOT NULL,
140
+ response_status INT DEFAULT NULL,
141
+ response_body LONGTEXT,
142
+ duration_ms INT DEFAULT NULL,
143
+ error TEXT,
144
+ created_at VARCHAR(40) NOT NULL,
145
+ CONSTRAINT {{fk_deliveries_webhook}} FOREIGN KEY (webhook_id) REFERENCES {{webhooks}}(id) ON DELETE CASCADE
146
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
147
+ CREATE INDEX deliveries_webhook ON {{webhook_deliveries}} (webhook_id);
148
+ CREATE INDEX deliveries_created ON {{webhook_deliveries}} (created_at DESC);
149
+
150
+ CREATE TABLE IF NOT EXISTS {{personal_access_tokens}} (
151
+ id BIGINT AUTO_INCREMENT PRIMARY KEY,
152
+ user_login VARCHAR(255) NOT NULL,
153
+ name VARCHAR(255) NOT NULL,
154
+ salt VARCHAR(32) NOT NULL,
155
+ token_hash VARCHAR(64) NOT NULL,
156
+ token_last_eight CHAR(8) NOT NULL,
157
+ scopes VARCHAR(2048) NOT NULL DEFAULT '[]',
158
+ ip_allowlist VARCHAR(2048) NOT NULL DEFAULT '[]',
159
+ expires_at VARCHAR(40) DEFAULT NULL,
160
+ last_used_at VARCHAR(40) DEFAULT NULL,
161
+ created_at VARCHAR(40) NOT NULL,
162
+ revoked_at VARCHAR(40) DEFAULT NULL,
163
+ CONSTRAINT {{fk_pat_user}} FOREIGN KEY (user_login) REFERENCES {{users}}(login) ON DELETE CASCADE
164
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
165
+ CREATE INDEX pat_user ON {{personal_access_tokens}} (user_login);
166
+ CREATE INDEX pat_last_eight ON {{personal_access_tokens}} (token_last_eight);
167
+
168
+ CREATE TABLE IF NOT EXISTS {{project_members}} (
169
+ project_id BIGINT NOT NULL,
170
+ user_login VARCHAR(255) NOT NULL,
171
+ role VARCHAR(16) NOT NULL DEFAULT 'writer'
172
+ CHECK (role IN ('reader','writer','admin')),
173
+ created_at VARCHAR(40) NOT NULL,
174
+ PRIMARY KEY (project_id, user_login),
175
+ CONSTRAINT {{fk_pm_project}} FOREIGN KEY (project_id) REFERENCES {{projects}}(id) ON DELETE CASCADE,
176
+ CONSTRAINT {{fk_pm_user}} FOREIGN KEY (user_login) REFERENCES {{users}}(login) ON DELETE CASCADE
177
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
178
+ CREATE INDEX project_members_user ON {{project_members}} (user_login);
package/src/schema.sql CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  -- login stays PRIMARY KEY intentionally. Renaming users is not supported;
5
5
  -- swap to INTEGER user_id + UNIQUE(login) if that ever changes.
6
- CREATE TABLE IF NOT EXISTS users (
6
+ CREATE TABLE IF NOT EXISTS {{users}} (
7
7
  login TEXT PRIMARY KEY,
8
8
  kind TEXT NOT NULL DEFAULT 'human'
9
9
  CHECK (kind IN ('human','bot','system')),
@@ -16,7 +16,7 @@ CREATE TABLE IF NOT EXISTS users (
16
16
  updated_at TEXT NOT NULL DEFAULT ''
17
17
  );
18
18
 
19
- CREATE TABLE IF NOT EXISTS projects (
19
+ CREATE TABLE IF NOT EXISTS {{projects}} (
20
20
  id INTEGER PRIMARY KEY AUTOINCREMENT,
21
21
  owner TEXT NOT NULL,
22
22
  name TEXT NOT NULL,
@@ -39,21 +39,21 @@ CREATE TABLE IF NOT EXISTS projects (
39
39
  -- the /settings page (or on first access if empty). Stored as rows rather
40
40
  -- than a single JSON blob so the settings UI can render a select without
41
41
  -- parsing JSON in SQL.
42
- CREATE TABLE IF NOT EXISTS model_cache (
42
+ CREATE TABLE IF NOT EXISTS {{model_cache}} (
43
43
  provider_model TEXT PRIMARY KEY,
44
44
  label TEXT NOT NULL,
45
45
  refreshed_at TEXT NOT NULL
46
46
  );
47
47
 
48
- CREATE TABLE IF NOT EXISTS issues (
48
+ CREATE TABLE IF NOT EXISTS {{issues}} (
49
49
  id INTEGER PRIMARY KEY AUTOINCREMENT,
50
- project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
50
+ project_id INTEGER NOT NULL REFERENCES {{projects}}(id) ON DELETE CASCADE,
51
51
  number INTEGER NOT NULL,
52
52
  title TEXT NOT NULL,
53
53
  body TEXT NOT NULL DEFAULT '',
54
54
  state TEXT NOT NULL DEFAULT 'open'
55
55
  CHECK (state IN ('open','closed')),
56
- author TEXT NOT NULL REFERENCES users(login),
56
+ author TEXT NOT NULL REFERENCES {{users}}(login),
57
57
  created_at TEXT NOT NULL,
58
58
  updated_at TEXT NOT NULL,
59
59
  -- NULL when open; stamped on close, cleared on reopen. Backfill-safe because
@@ -62,63 +62,63 @@ CREATE TABLE IF NOT EXISTS issues (
62
62
  UNIQUE (project_id, number)
63
63
  );
64
64
  CREATE INDEX IF NOT EXISTS issues_project_state_updated
65
- ON issues (project_id, state, updated_at DESC);
65
+ ON {{issues}} (project_id, state, updated_at DESC);
66
66
  CREATE INDEX IF NOT EXISTS issues_state_updated
67
- ON issues (state, updated_at DESC);
67
+ ON {{issues}} (state, updated_at DESC);
68
68
 
69
- CREATE TABLE IF NOT EXISTS comments (
69
+ CREATE TABLE IF NOT EXISTS {{comments}} (
70
70
  id INTEGER PRIMARY KEY AUTOINCREMENT,
71
- issue_id INTEGER NOT NULL REFERENCES issues(id) ON DELETE CASCADE,
72
- author TEXT NOT NULL REFERENCES users(login),
71
+ issue_id INTEGER NOT NULL REFERENCES {{issues}}(id) ON DELETE CASCADE,
72
+ author TEXT NOT NULL REFERENCES {{users}}(login),
73
73
  body TEXT NOT NULL,
74
74
  created_at TEXT NOT NULL,
75
75
  updated_at TEXT NOT NULL DEFAULT ''
76
76
  );
77
77
  CREATE INDEX IF NOT EXISTS comments_issue_created
78
- ON comments (issue_id, created_at);
78
+ ON {{comments}} (issue_id, created_at);
79
79
 
80
- CREATE TABLE IF NOT EXISTS labels (
80
+ CREATE TABLE IF NOT EXISTS {{labels}} (
81
81
  id INTEGER PRIMARY KEY AUTOINCREMENT,
82
- project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
82
+ project_id INTEGER NOT NULL REFERENCES {{projects}}(id) ON DELETE CASCADE,
83
83
  name TEXT NOT NULL,
84
84
  color TEXT NOT NULL DEFAULT '#888888',
85
85
  UNIQUE (project_id, name)
86
86
  );
87
87
 
88
- CREATE TABLE IF NOT EXISTS issue_labels (
89
- issue_id INTEGER NOT NULL REFERENCES issues(id) ON DELETE CASCADE,
90
- label_id INTEGER NOT NULL REFERENCES labels(id) ON DELETE CASCADE,
88
+ CREATE TABLE IF NOT EXISTS {{issue_labels}} (
89
+ issue_id INTEGER NOT NULL REFERENCES {{issues}}(id) ON DELETE CASCADE,
90
+ label_id INTEGER NOT NULL REFERENCES {{labels}}(id) ON DELETE CASCADE,
91
91
  PRIMARY KEY (issue_id, label_id)
92
92
  );
93
93
 
94
- CREATE TABLE IF NOT EXISTS reactions (
95
- comment_id INTEGER NOT NULL REFERENCES comments(id) ON DELETE CASCADE,
96
- user_login TEXT NOT NULL REFERENCES users(login),
94
+ CREATE TABLE IF NOT EXISTS {{reactions}} (
95
+ comment_id INTEGER NOT NULL REFERENCES {{comments}}(id) ON DELETE CASCADE,
96
+ user_login TEXT NOT NULL REFERENCES {{users}}(login),
97
97
  content TEXT NOT NULL,
98
98
  PRIMARY KEY (comment_id, user_login, content)
99
99
  );
100
100
  CREATE INDEX IF NOT EXISTS reactions_comment
101
- ON reactions (comment_id);
101
+ ON {{reactions}} (comment_id);
102
102
 
103
- CREATE TABLE IF NOT EXISTS attachments (
103
+ CREATE TABLE IF NOT EXISTS {{attachments}} (
104
104
  uuid TEXT PRIMARY KEY,
105
- issue_id INTEGER NOT NULL REFERENCES issues(id) ON DELETE CASCADE,
105
+ issue_id INTEGER NOT NULL REFERENCES {{issues}}(id) ON DELETE CASCADE,
106
106
  filename TEXT NOT NULL,
107
107
  content_type TEXT NOT NULL DEFAULT 'application/octet-stream',
108
108
  size INTEGER NOT NULL,
109
109
  blob_path TEXT NOT NULL,
110
- uploaded_by TEXT NOT NULL REFERENCES users(login),
110
+ uploaded_by TEXT NOT NULL REFERENCES {{users}}(login),
111
111
  created_at TEXT NOT NULL
112
112
  );
113
113
  CREATE INDEX IF NOT EXISTS attachments_issue
114
- ON attachments (issue_id);
114
+ ON {{attachments}} (issue_id);
115
115
 
116
116
  -- Webhooks (Gitea-compatible). Scoped per-project so different repos can fan out
117
117
  -- to different downstream consumers. `events` is a JSON array of event types
118
118
  -- ('issues', 'issue_comment', 'push', ...). ework v1 emits only the first two.
119
- CREATE TABLE IF NOT EXISTS webhooks (
119
+ CREATE TABLE IF NOT EXISTS {{webhooks}} (
120
120
  id INTEGER PRIMARY KEY AUTOINCREMENT,
121
- project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
121
+ project_id INTEGER NOT NULL REFERENCES {{projects}}(id) ON DELETE CASCADE,
122
122
  url TEXT NOT NULL,
123
123
  secret TEXT NOT NULL DEFAULT '',
124
124
  content_type TEXT NOT NULL DEFAULT 'application/json',
@@ -129,13 +129,13 @@ CREATE TABLE IF NOT EXISTS webhooks (
129
129
  updated_at TEXT NOT NULL
130
130
  );
131
131
  CREATE INDEX IF NOT EXISTS webhooks_project
132
- ON webhooks (project_id);
132
+ ON {{webhooks}} (project_id);
133
133
 
134
134
  -- Delivery history. One row per attempt. Retries append new rows (don't overwrite),
135
135
  -- so a failed webhook shows the full retry trail.
136
- CREATE TABLE IF NOT EXISTS webhook_deliveries (
136
+ CREATE TABLE IF NOT EXISTS {{webhook_deliveries}} (
137
137
  id INTEGER PRIMARY KEY AUTOINCREMENT,
138
- webhook_id INTEGER NOT NULL REFERENCES webhooks(id) ON DELETE CASCADE,
138
+ webhook_id INTEGER NOT NULL REFERENCES {{webhooks}}(id) ON DELETE CASCADE,
139
139
  event TEXT NOT NULL,
140
140
  delivery_uuid TEXT NOT NULL,
141
141
  payload TEXT NOT NULL,
@@ -146,17 +146,17 @@ CREATE TABLE IF NOT EXISTS webhook_deliveries (
146
146
  created_at TEXT NOT NULL
147
147
  );
148
148
  CREATE INDEX IF NOT EXISTS deliveries_webhook
149
- ON webhook_deliveries (webhook_id);
149
+ ON {{webhook_deliveries}} (webhook_id);
150
150
  CREATE INDEX IF NOT EXISTS deliveries_created
151
- ON webhook_deliveries (created_at DESC);
151
+ ON {{webhook_deliveries}} (created_at DESC);
152
152
 
153
153
  -- Personal Access Tokens (Gitea-aligned). Hashed with per-token salt so the
154
154
  -- DB leak doesn't reveal tokens; last_eight enables indexed lookup without
155
155
  -- storing the plaintext. `scopes` is stored but not yet enforced — every PAT
156
156
  -- inherits the user's full perms in v1; granularity lands with project_members.
157
- CREATE TABLE IF NOT EXISTS personal_access_tokens (
157
+ CREATE TABLE IF NOT EXISTS {{personal_access_tokens}} (
158
158
  id INTEGER PRIMARY KEY AUTOINCREMENT,
159
- user_login TEXT NOT NULL REFERENCES users(login) ON DELETE CASCADE,
159
+ user_login TEXT NOT NULL REFERENCES {{users}}(login) ON DELETE CASCADE,
160
160
  name TEXT NOT NULL,
161
161
  salt TEXT NOT NULL,
162
162
  token_hash TEXT NOT NULL,
@@ -171,9 +171,9 @@ CREATE TABLE IF NOT EXISTS personal_access_tokens (
171
171
  revoked_at TEXT
172
172
  );
173
173
  CREATE INDEX IF NOT EXISTS pat_user
174
- ON personal_access_tokens (user_login);
174
+ ON {{personal_access_tokens}} (user_login);
175
175
  CREATE INDEX IF NOT EXISTS pat_last_eight
176
- ON personal_access_tokens (token_last_eight);
176
+ ON {{personal_access_tokens}} (token_last_eight);
177
177
 
178
178
  -- Per-project RBAC. Roles follow Gitea semantics:
179
179
  -- reader: can read issues + comments (currently no-op since all authed users
@@ -183,13 +183,13 @@ CREATE INDEX IF NOT EXISTS pat_last_eight
183
183
  -- Site-admins (users.is_admin=1) bypass all checks. PAT scope enforcement also
184
184
  -- routes through here: a write-scoped PAT can only write where the owning user
185
185
  -- has writer+ role on the target project.
186
- CREATE TABLE IF NOT EXISTS project_members (
187
- project_id INTEGER NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
188
- user_login TEXT NOT NULL REFERENCES users(login) ON DELETE CASCADE,
186
+ CREATE TABLE IF NOT EXISTS {{project_members}} (
187
+ project_id INTEGER NOT NULL REFERENCES {{projects}}(id) ON DELETE CASCADE,
188
+ user_login TEXT NOT NULL REFERENCES {{users}}(login) ON DELETE CASCADE,
189
189
  role TEXT NOT NULL DEFAULT 'writer'
190
190
  CHECK (role IN ('reader','writer','admin')),
191
191
  created_at TEXT NOT NULL,
192
192
  PRIMARY KEY (project_id, user_login)
193
193
  );
194
194
  CREATE INDEX IF NOT EXISTS project_members_user
195
- ON project_members (user_login);
195
+ ON {{project_members}} (user_login);