@hasna/shortlinks 0.1.4 → 0.1.6
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 +11 -0
- package/dist/cli/index.js +0 -7
- package/dist/index.js +0 -7
- package/infra/aws-ec2-user-data.sh +168 -0
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -138,6 +138,17 @@ shortlinks cloud sync
|
|
|
138
138
|
|
|
139
139
|
The cloud database service name is `shortlinks`.
|
|
140
140
|
|
|
141
|
+
## AWS Origin
|
|
142
|
+
|
|
143
|
+
For an apex domain that needs stable A records, `infra/aws-ec2-user-data.sh` bootstraps a small EC2 redirect origin with:
|
|
144
|
+
|
|
145
|
+
- `@hasna/shortlinks` installed through Bun
|
|
146
|
+
- local SQLite data synced with the `shortlinks` RDS database through `@hasna/cloud`
|
|
147
|
+
- Caddy terminating HTTPS and proxying to `shortlinks serve`
|
|
148
|
+
- a systemd timer that syncs links and clicks every minute
|
|
149
|
+
|
|
150
|
+
The script reads the RDS password from AWS Secrets Manager through the instance role; it does not contain secret values.
|
|
151
|
+
|
|
141
152
|
## Development
|
|
142
153
|
|
|
143
154
|
```bash
|
package/dist/cli/index.js
CHANGED
|
@@ -3435,13 +3435,6 @@ var PG_MIGRATIONS = [
|
|
|
3435
3435
|
CREATE INDEX IF NOT EXISTS idx_clicks_domain ON clicks(domain_id);
|
|
3436
3436
|
CREATE INDEX IF NOT EXISTS idx_clicks_clicked_at ON clicks(clicked_at);
|
|
3437
3437
|
CREATE INDEX IF NOT EXISTS idx_clicks_updated ON clicks(updated_at);
|
|
3438
|
-
|
|
3439
|
-
CREATE TABLE IF NOT EXISTS _pg_migrations (
|
|
3440
|
-
id INTEGER PRIMARY KEY,
|
|
3441
|
-
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
3442
|
-
);
|
|
3443
|
-
|
|
3444
|
-
INSERT INTO _pg_migrations (id) VALUES (1) ON CONFLICT DO NOTHING;
|
|
3445
3438
|
`
|
|
3446
3439
|
];
|
|
3447
3440
|
|
package/dist/index.js
CHANGED
|
@@ -838,13 +838,6 @@ var PG_MIGRATIONS = [
|
|
|
838
838
|
CREATE INDEX IF NOT EXISTS idx_clicks_domain ON clicks(domain_id);
|
|
839
839
|
CREATE INDEX IF NOT EXISTS idx_clicks_clicked_at ON clicks(clicked_at);
|
|
840
840
|
CREATE INDEX IF NOT EXISTS idx_clicks_updated ON clicks(updated_at);
|
|
841
|
-
|
|
842
|
-
CREATE TABLE IF NOT EXISTS _pg_migrations (
|
|
843
|
-
id INTEGER PRIMARY KEY,
|
|
844
|
-
applied_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
845
|
-
);
|
|
846
|
-
|
|
847
|
-
INSERT INTO _pg_migrations (id) VALUES (1) ON CONFLICT DO NOTHING;
|
|
848
841
|
`
|
|
849
842
|
];
|
|
850
843
|
export {
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
export AWS_REGION="${AWS_REGION:-us-east-1}"
|
|
5
|
+
export SHORTLINKS_HOME="/var/lib/shortlinks"
|
|
6
|
+
export SHORTLINKS_PACKAGE="@hasna/shortlinks@latest"
|
|
7
|
+
export RDS_SECRET_ID="rds!db-7a451ce6-83a9-40fa-b24a-81e5d5943511"
|
|
8
|
+
export RDS_HOST="hasnaxyz-prod-opensource.c4limg0qgqvk.us-east-1.rds.amazonaws.com"
|
|
9
|
+
export RDS_USERNAME="hasna_admin"
|
|
10
|
+
|
|
11
|
+
dnf update -y
|
|
12
|
+
dnf install -y awscli jq tar gzip shadow-utils libcap
|
|
13
|
+
|
|
14
|
+
if ! id shortlinks >/dev/null 2>&1; then
|
|
15
|
+
useradd --system --create-home --home-dir "${SHORTLINKS_HOME}" --shell /sbin/nologin shortlinks
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
install -d -o shortlinks -g shortlinks "${SHORTLINKS_HOME}/.hasna/cloud"
|
|
19
|
+
install -d -o shortlinks -g shortlinks "${SHORTLINKS_HOME}/.hasna/shortlinks"
|
|
20
|
+
|
|
21
|
+
cat > "${SHORTLINKS_HOME}/.hasna/cloud/config.json" <<CLOUD_CONFIG
|
|
22
|
+
{
|
|
23
|
+
"rds": {
|
|
24
|
+
"host": "${RDS_HOST}",
|
|
25
|
+
"port": 5432,
|
|
26
|
+
"username": "${RDS_USERNAME}",
|
|
27
|
+
"password_env": "HASNA_RDS_PASSWORD",
|
|
28
|
+
"ssl": true
|
|
29
|
+
},
|
|
30
|
+
"mode": "hybrid",
|
|
31
|
+
"feedback_endpoint": "https://feedback.hasna.com/api/v1/feedback",
|
|
32
|
+
"auto_sync_interval_minutes": 0,
|
|
33
|
+
"sync": {
|
|
34
|
+
"schedule_minutes": 0
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
CLOUD_CONFIG
|
|
38
|
+
chown shortlinks:shortlinks "${SHORTLINKS_HOME}/.hasna/cloud/config.json"
|
|
39
|
+
chmod 600 "${SHORTLINKS_HOME}/.hasna/cloud/config.json"
|
|
40
|
+
|
|
41
|
+
su -s /bin/bash shortlinks -c 'curl -fsSL https://bun.sh/install | bash'
|
|
42
|
+
su -s /bin/bash shortlinks -c "${SHORTLINKS_HOME}/.bun/bin/bun install -g ${SHORTLINKS_PACKAGE} --no-cache"
|
|
43
|
+
|
|
44
|
+
cat > /usr/local/bin/shortlinks-env-exec <<'RUNNER'
|
|
45
|
+
#!/usr/bin/env bash
|
|
46
|
+
set -euo pipefail
|
|
47
|
+
|
|
48
|
+
export AWS_REGION="${AWS_REGION:-us-east-1}"
|
|
49
|
+
export HOME="/var/lib/shortlinks"
|
|
50
|
+
export PATH="/var/lib/shortlinks/.bun/bin:/usr/local/bin:/usr/bin:/bin"
|
|
51
|
+
export NODE_TLS_REJECT_UNAUTHORIZED="0"
|
|
52
|
+
|
|
53
|
+
secret_json="$(aws secretsmanager get-secret-value \
|
|
54
|
+
--region "${AWS_REGION}" \
|
|
55
|
+
--secret-id "rds!db-7a451ce6-83a9-40fa-b24a-81e5d5943511" \
|
|
56
|
+
--query SecretString \
|
|
57
|
+
--output text)"
|
|
58
|
+
|
|
59
|
+
export HASNA_RDS_PASSWORD
|
|
60
|
+
HASNA_RDS_PASSWORD="$(jq -r '.password' <<<"${secret_json}")"
|
|
61
|
+
|
|
62
|
+
exec "$@"
|
|
63
|
+
RUNNER
|
|
64
|
+
chmod 750 /usr/local/bin/shortlinks-env-exec
|
|
65
|
+
chown root:shortlinks /usr/local/bin/shortlinks-env-exec
|
|
66
|
+
|
|
67
|
+
su -s /bin/bash shortlinks -c '/usr/local/bin/shortlinks-env-exec shortlinks --json doctor'
|
|
68
|
+
su -s /bin/bash shortlinks -c '/usr/local/bin/shortlinks-env-exec shortlinks --json config set default-domain has.na'
|
|
69
|
+
su -s /bin/bash shortlinks -c '/usr/local/bin/shortlinks-env-exec shortlinks --json cloud pull --tables domains,links,clicks' || true
|
|
70
|
+
|
|
71
|
+
caddy_version="$(curl -fsSL https://api.github.com/repos/caddyserver/caddy/releases/latest | jq -r '.tag_name // "v2.10.2"' | sed 's/^v//')"
|
|
72
|
+
case "$(uname -m)" in
|
|
73
|
+
aarch64|arm64) caddy_arch="arm64" ;;
|
|
74
|
+
x86_64|amd64) caddy_arch="amd64" ;;
|
|
75
|
+
*) caddy_arch="arm64" ;;
|
|
76
|
+
esac
|
|
77
|
+
curl -fsSL -o /tmp/caddy.tar.gz "https://github.com/caddyserver/caddy/releases/download/v${caddy_version}/caddy_${caddy_version}_linux_${caddy_arch}.tar.gz"
|
|
78
|
+
tar -xzf /tmp/caddy.tar.gz -C /tmp caddy
|
|
79
|
+
install -m 0755 /tmp/caddy /usr/local/bin/caddy
|
|
80
|
+
setcap cap_net_bind_service=+ep /usr/local/bin/caddy || true
|
|
81
|
+
|
|
82
|
+
cat > /etc/systemd/system/shortlinks.service <<'SERVICE'
|
|
83
|
+
[Unit]
|
|
84
|
+
Description=Shortlinks redirect server
|
|
85
|
+
After=network-online.target
|
|
86
|
+
Wants=network-online.target
|
|
87
|
+
|
|
88
|
+
[Service]
|
|
89
|
+
Type=simple
|
|
90
|
+
User=shortlinks
|
|
91
|
+
Group=shortlinks
|
|
92
|
+
WorkingDirectory=/var/lib/shortlinks
|
|
93
|
+
Environment=HOME=/var/lib/shortlinks
|
|
94
|
+
Environment=PATH=/var/lib/shortlinks/.bun/bin:/usr/local/bin:/usr/bin:/bin
|
|
95
|
+
ExecStartPre=/usr/local/bin/shortlinks-env-exec shortlinks --json cloud pull --tables domains,links
|
|
96
|
+
ExecStart=/usr/local/bin/shortlinks-env-exec shortlinks serve --host 127.0.0.1 --port 8787 --default-host has.na
|
|
97
|
+
Restart=always
|
|
98
|
+
RestartSec=5
|
|
99
|
+
|
|
100
|
+
[Install]
|
|
101
|
+
WantedBy=multi-user.target
|
|
102
|
+
SERVICE
|
|
103
|
+
|
|
104
|
+
cat > /etc/systemd/system/shortlinks-sync.service <<'SERVICE'
|
|
105
|
+
[Unit]
|
|
106
|
+
Description=Sync shortlinks SQLite data with RDS
|
|
107
|
+
After=network-online.target
|
|
108
|
+
Wants=network-online.target
|
|
109
|
+
|
|
110
|
+
[Service]
|
|
111
|
+
Type=oneshot
|
|
112
|
+
User=shortlinks
|
|
113
|
+
Group=shortlinks
|
|
114
|
+
WorkingDirectory=/var/lib/shortlinks
|
|
115
|
+
Environment=HOME=/var/lib/shortlinks
|
|
116
|
+
Environment=PATH=/var/lib/shortlinks/.bun/bin:/usr/local/bin:/usr/bin:/bin
|
|
117
|
+
ExecStart=/usr/local/bin/shortlinks-env-exec shortlinks --json cloud sync --tables domains,links,clicks
|
|
118
|
+
SERVICE
|
|
119
|
+
|
|
120
|
+
cat > /etc/systemd/system/shortlinks-sync.timer <<'TIMER'
|
|
121
|
+
[Unit]
|
|
122
|
+
Description=Run shortlinks cloud sync every minute
|
|
123
|
+
|
|
124
|
+
[Timer]
|
|
125
|
+
OnBootSec=2min
|
|
126
|
+
OnUnitActiveSec=1min
|
|
127
|
+
Unit=shortlinks-sync.service
|
|
128
|
+
|
|
129
|
+
[Install]
|
|
130
|
+
WantedBy=timers.target
|
|
131
|
+
TIMER
|
|
132
|
+
|
|
133
|
+
cat > /etc/systemd/system/caddy.service <<'SERVICE'
|
|
134
|
+
[Unit]
|
|
135
|
+
Description=Caddy web server for shortlinks
|
|
136
|
+
After=network-online.target shortlinks.service
|
|
137
|
+
Wants=network-online.target
|
|
138
|
+
|
|
139
|
+
[Service]
|
|
140
|
+
Type=simple
|
|
141
|
+
User=root
|
|
142
|
+
Group=root
|
|
143
|
+
ExecStart=/usr/local/bin/caddy run --environ --config /etc/caddy/Caddyfile
|
|
144
|
+
ExecReload=/usr/local/bin/caddy reload --config /etc/caddy/Caddyfile --force
|
|
145
|
+
TimeoutStopSec=5s
|
|
146
|
+
LimitNOFILE=1048576
|
|
147
|
+
PrivateTmp=true
|
|
148
|
+
ProtectSystem=full
|
|
149
|
+
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
|
150
|
+
Restart=on-failure
|
|
151
|
+
|
|
152
|
+
[Install]
|
|
153
|
+
WantedBy=multi-user.target
|
|
154
|
+
SERVICE
|
|
155
|
+
|
|
156
|
+
install -d /etc/caddy
|
|
157
|
+
cat > /etc/caddy/Caddyfile <<'CADDY'
|
|
158
|
+
has.na {
|
|
159
|
+
encode zstd gzip
|
|
160
|
+
reverse_proxy 127.0.0.1:8787
|
|
161
|
+
}
|
|
162
|
+
CADDY
|
|
163
|
+
|
|
164
|
+
systemctl daemon-reload
|
|
165
|
+
systemctl enable shortlinks.service shortlinks-sync.timer caddy.service
|
|
166
|
+
systemctl start shortlinks.service
|
|
167
|
+
systemctl start shortlinks-sync.timer
|
|
168
|
+
systemctl start caddy.service || true
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/shortlinks",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "CLI-only shortlink manager for custom domains, click tracking, Cloudflare setup, and @hasna cloud sync",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"files": [
|
|
26
26
|
"dist",
|
|
27
27
|
"cloudflare",
|
|
28
|
+
"infra",
|
|
28
29
|
"LICENSE",
|
|
29
30
|
"README.md",
|
|
30
31
|
"SECURITY.md"
|