This commit is contained in:
2025-03-11 19:01:30 +08:00
commit 9b4f218e77
30 changed files with 2892 additions and 0 deletions

177
.gitignore vendored Normal file
View File

@@ -0,0 +1,177 @@
# Created by https://www.toptal.com/developers/gitignore/api/python
# Edit at https://www.toptal.com/developers/gitignore?templates=python
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
### Python Patch ###
# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
poetry.toml
# ruff
.ruff_cache/
# LSP config files
pyrightconfig.json
# End of https://www.toptal.com/developers/gitignore/api/python

1
.python-version Normal file
View File

@@ -0,0 +1 @@
3.11.3

7
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"yaml.schemas": {
"https://taskfile.dev/schema.json": "file:///Users/cuichenyang/Workspace/projet/safeline/Taskfile"
},
"python.analysis.typeCheckingMode": "basic",
"python.analysis.autoImportCompletions": true
}

20
Dockerfile Normal file
View File

@@ -0,0 +1,20 @@
FROM chaitin/safeline-mgt:latest as base
FROM python:3.10-alpine3.14 as replacer
RUN cd /tmp &&\
wget https://github.com/upx/upx/releases/download/v4.2.4/upx-4.2.4-amd64_linux.tar.xz &&\
tar -xvf upx-4.2.4-amd64_linux.tar.xz &&\
mv upx-4.2.4-amd64_linux/upx /usr/bin/ &&\
rm -rf upx-4.2.4-amd64_linux.tar.xz upx-4.2.4-amd64_linux
COPY --from=base /app/mgt /mgt
COPY replace.py /
RUN upx -d /mgt &&\
python /replace.py /mgt
FROM base
COPY --from=replacer /mgt /app/mgt

0
README.md Normal file
View File

2
activate/Makefile Normal file
View File

@@ -0,0 +1,2 @@
pb:
protoc --go_out=. --go_opt=paths=import --go-grpc_out=. --go-grpc_opt=paths=import -I./protos ./protos/*

18
activate/certs/cert_0.pem Normal file
View File

@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC4TCCAcmgAwIBAgIBATANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDDA5zYWZl
b3BzX3NlcnZlcjAgFw0yNDAxMTkwODM2MDVaGA8yMTI0MDExOTA4MzYwNVowGTEX
MBUGA1UEAwwOc2FmZW9wc19zZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCaqqyHlYCWGlB1vtuknX8d6NhigmQgC9t+y7tp5zMC5VBgVzmji6qn
i5WmIjNq5jUlWecKAqbcP19WqP8z/fwi/QiWmVeHTPDEZPNkwpD9G8J1vjPONmTq
Ut3qFb81CYKKWEhWkEP/Jm7cX+6GDElNBjllp01JKfhnQ2VG6M+fRNvlyhizazUI
eag49NSlnr14A7ejBhfa4JbqRaQrqy9gnVauKKFklebraauQt9Ifoimqsrs0kqnA
TEiUdoahQgjU8VeRRvbGZH02hFCKxTEIoQjOxvHzwGliEUc5nUv+J0m4Heg3tXCh
hBB82fZXZuJBTOrjLHPPis5am6H1cyS1AgMBAAGjMjAwMA8GA1UdEwEB/wQFMAMB
Af8wHQYDVR0OBBYEFHxF5K84izQk5BV+SoVs8Zh+p/PAMA0GCSqGSIb3DQEBCwUA
A4IBAQAZPWz44RRhc80YzW5WeEI3WCJ44D7C90QBUb24v3N7Qr/9TejvuAQFyLCq
bZulGUHKw3SbqxeDkpU2ExKDHqQRX32JtZPWLFt8E9Gnv6Ahjh3KpjitIRR/hr0i
Z4aFijOXe/5Y+eeJOeoc4eqKms6U0qwBmYLMbz/crtvq1QcA/46gqNmnTPO+2hsa
hrP5d86AqPR+l34YIBltQjgivNY0+XmOEXns1ldtCQfUzsrVL4oBRRixLUoz2yWh
kdSWZv1aIfi0Qjv5chXvkmPFKvreSDwQ9vWEpjmWH9Qk3iUJ/q5SunYJrSCD/Wq2
STbwVsMLE5a2mrvxshlrzHD4mo/W
-----END CERTIFICATE-----

18
activate/certs/cert_1.pem Normal file
View File

@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC4TCCAcmgAwIBAgIBATANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDDA5zYWZl
b3BzX3NlcnZlcjAgFw0yNDAxMjYxMDA0MzhaGA8yMTI0MDEyNjEwMDQzOFowGTEX
MBUGA1UEAwwOc2FmZW9wc19zZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCvcDcMN9D0uB2BH5KiGan7qy/g/lnXHalZGFyQMiuttBVuUH1OQzZI
2v+aUBBjBEM9QE/KqMLiSf/ht2fq1nnME5ibN4Bq/+/BZsFYh3zwKya+g9HoiiTh
EOcDI7x1UgK0vNvkYNK0j90CEpooUQr6uJo6cAi5tSk8jKTHXHN69UMlexHHJx3O
+2KVUVd20eD6hijw1nUm7vELtKgVm+OPhgIpJd8JgLMy/+aHSJ5LGn0fgEK1EDSK
QNbq6XgBxyFzjE3nm3kQoW1TQ4bCM7KYlsVX8paXGNtHutk5QSPMzSu8bddN1FiV
9gTS8a2tyH57I0D8irrSY2a0+8uwK6zvAgMBAAGjMjAwMA8GA1UdEwEB/wQFMAMB
Af8wHQYDVR0OBBYEFB87MQdFVXygzHgbAvEO1qPGY+okMA0GCSqGSIb3DQEBCwUA
A4IBAQAZTZE9yDNebpllAOHyJKp8KDnGUX/KFhtD91B2bQ2ETGGcSdVs9EO6enDZ
DObe5m1bH/FNS5TcB4E2bMGWFul7jYIQyIlN92zFMjHEj/X2WaU0QaLFmCAJaQZN
Jqi/iCrn1KWZIH6xfQNMyRE2251uMTFQRDTxasBjWQ0kIIVsDT4tVdYG1X2Ea7kF
FezHTYptGj09EOFkJ+ppZoLlAQopwtsxwt92c9FiBhY5F+xMhj7SHiEPGpxNLIV5
lX7D4N+HW8XKb9Iob+b+d4q2XRnZuFLuQdxV98pKwxDtriDdS4r0TCVjSMJk1u/w
TvtvK1/QTQycBNzawo11TvLiZTir
-----END CERTIFICATE-----

18
activate/certs/cert_2.pem Normal file
View File

@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC6jCCAdKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAZMRcwFQYDVQQDDA5zYWZl
b3BzX3NlcnZlcjAgFw0yNDAxMjYxMDA0MzhaGA8yMTI0MDEyNjEwMDQzOFowGTEX
MBUGA1UEAwwOc2FmZW9wc19zZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQCz/45yQ35YomeGgO80Cg1S7F6kiNboTClCtrKBale98sKaKCoiV+gG
7vYKOTn/5DhDmawHATe1ENtrkAl3eAjvE0uHpcRnSi2B1Bcwt0r+7YyrxqoMCDiX
Bja8gpzXn4aZiDEYtdD5jVAq7N2wkVnHEdGyqGByh3M0pmUPUBNTTKzv7YGUfQeG
zERKlv0I7B06OS2LwYOLb6Xg70I9CxqPrqwelRGcbrFP4IyrM6C5QrTue1fMy30u
XFIxYS5SWjWys314rzWmJ+z7lmal3cGh+3oc7nhlxVlT9gWgc6V0t2+lVazhmCPQ
vXW64HjEoQvvmPVzkMhvbt8dBQUcV5dbAgMBAAGjOzA5MA4GA1UdDwEB/wQEAwIH
gDAMBgNVHRMBAf8EAjAAMBkGA1UdEQQSMBCCDnNhZmVvcHNfc2VydmVyMA0GCSqG
SIb3DQEBCwUAA4IBAQBPbR7pJjADGMHhZi//gylpbGF7XmZL58OC5Qba2IuO5JWF
Be8gO1A+PUsMHavUEkd2LGxZUWLc0j2fCtSq4Ui7Hck6tbpHwnnny83pr6jjpqZ1
PIeicmukRrIuSVHvw9gDI3HvBmHDlMbjOn5/ZrCGJv5flv+rvaRdIOXe4fQfPWg4
oD4OM2C3yycln8QWAdAOxsrdsTeyEnJFlMRk/98y5KHCrlg8q9jVNq/jkl1a+0xF
Dh+RyGl+v+830AeQWj2uWF+svTlWD33oF5srkeWK24JYmbKRwT/6+RrXjfrDDcWg
VfQr+GwlipY7dAqT8pYWCmmL8GCRui+FeBTUezlF
-----END CERTIFICATE-----

18
activate/certs/cert_3.pem Normal file
View File

@@ -0,0 +1,18 @@
-----BEGIN CERTIFICATE-----
MIIC9jCCAd4CFGOjD+kDjDesBb0h+iV806oSwI72MA0GCSqGSIb3DQEBCwUAMBkx
FzAVBgNVBAMMDnNhZmVvcHNfc2VydmVyMB4XDTI0MDEyNjA4MDczMVoXDTI1MDEy
NTA4MDczMVowVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAf
BgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGMTIzNDU2
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxAe4pElf6eM0XLE5/VEo
o4GDz5w0up6a04iZm0wddGQYOuhFRewWxOoA/5npuZLE9APgWZM0aBseptyrwhsf
gnbl861Fb2Fs4FkhRsjJT7dgDhZ5UkwnaeHuhaaakQz+OvufwStHjeJnZ5p7KU4U
LFa2BCAXAgIusOZX6FY//RfVo9fEyduubR1OGOiScx8z3ZyNoPx6WvM0T1H18cOi
fb9ORHTV3arADT3FaOJB/lLcuMLedPu191mYDaHjlhQs8ub4l01OgP2a21hidYBc
GcfP18Q1zyBozCDxt6OGvHN/2ohmut4aU+c4AULEqjIs4y3D/9FfhjhzXDq04ZP7
AQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAnZDveQKjiL7UUMrSxvKcrkrcCgKxt
3HbnwSIyu+8fON3pyN2+YC/brFjbat+Ykvb+jxPxtoZg5cy+olR1RvUrQUmN62j9
5tEFr4xHJYP75dSyrYyfQ1flUJJlgzZN9SRIqQfnYySn+mLkzrAIpPNWywNpqPi5
zQ1GuXSDBLPLnTrTO8eK+XpXbOIGW4lHfnOfqBXOcVh1Alxh78YTg0BzmyUUtg/j
nf3pxZOWq6UgLNfOPCalbTacX5PguVtMaloA1A0CiU9tGPd9EOH3FkEKV4QE2nMW
2DsCkgczQOyaxaUy7iJk+wmbLMlwTREalMkndNuaX4h/e5MAOnR6Ev63
-----END CERTIFICATE-----

28
activate/certs/certs.go Normal file
View File

@@ -0,0 +1,28 @@
package certs
import (
"crypto/tls"
"crypto/x509"
"fmt"
"google.golang.org/grpc/credentials"
)
func LoadTransportCredentials() (credentials.TransportCredentials, error) {
cert, err := tls.X509KeyPair(Cert_2, Key_1)
if err != nil {
return nil, err
}
certPool := x509.NewCertPool()
if ok := certPool.AppendCertsFromPEM(Cert_1); !ok {
return nil, fmt.Errorf("failed to append ca certs")
}
return credentials.NewTLS(&tls.Config{
ServerName: "safeops_server",
Certificates: []tls.Certificate{cert},
RootCAs: certPool,
ClientAuth: tls.NoClientCert,
}), nil
}

14
activate/certs/embed.go Normal file
View File

@@ -0,0 +1,14 @@
package certs
import (
_ "embed"
)
//go:embed cert_2.pem
var Cert_2 []byte
//go:embed key_1.pem
var Key_1 []byte
//go:embed cert_1.pem
var Cert_1 []byte

28
activate/certs/key_0.pem Normal file
View File

@@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDEB7ikSV/p4zRc
sTn9USijgYPPnDS6nprTiJmbTB10ZBg66EVF7BbE6gD/mem5ksT0A+BZkzRoGx6m
3KvCGx+CduXzrUVvYWzgWSFGyMlPt2AOFnlSTCdp4e6FppqRDP46+5/BK0eN4mdn
mnspThQsVrYEIBcCAi6w5lfoVj/9F9Wj18TJ265tHU4Y6JJzHzPdnI2g/Hpa8zRP
UfXxw6J9v05EdNXdqsANPcVo4kH+Uty4wt50+7X3WZgNoeOWFCzy5viXTU6A/Zrb
WGJ1gFwZx8/XxDXPIGjMIPG3o4a8c3/aiGa63hpT5zgBQsSqMizjLcP/0V+GOHNc
OrThk/sBAgMBAAECggEAAPaQv63knL+TCEmOoWH+sx71yNVZpPkWhaeMKaksE+BS
BigR/w/z5K/JpXfaICFKenByb747aa+IbQV0ipWbiAaRhqr5RlzSLiAB+9kMtCmQ
fUE7A16AaxLDG0EATWok7aFC3a4aweW04Ftv83oAu18Jsed+dXIRTKk1EV0rH1E2
c0sKwpPIOC7N4xIXqiZ7KQ7JFCrlwIv2LFV/XFFFhTGqF5UHNsr4EemzNDAHFoz8
lgfhOC4JyF3ErtumSkXrWE11F911WjltUSpNV7Au4Stqco8tSrRVMInyQLaXsJSd
0RkAzznoQD6PVrYOEA9ktkVfYg98fjRf/EZ3JINNiQKBgQD26RnSlDjMfiGXq7Z5
b92ublB6H35vfxOJQ3kzf/4v+TcgkCmAgbUphpuNw2vPBdRFDl56PxtT3wEKo0FD
NbOIMcYqe/h9q1DxjUx/XiNhU7lFm04SV6vr1EyfBBdFx4KIqq58UPyb8G6wITuJ
vBP4gTjrJW6JU0NFjeO3pPOC9wKBgQDLPx7lyqvKHEf65g6IH8LdQTp6Gig+LsjV
vjILvABom3DlKRF1TYDT1lHTj9ARhFxMH6+4b7V7NuoA/f0hO6O61sBKq5ahYpH+
soXrDJwhcyOD5U6BX+cOj5KASe6EZZebMMoWwMOyvXWunbpKvUIphN7ce+GaBb73
OhsHluf7xwKBgQD2p4QpTfKzrejm+B2QkITW5NK2PNH0lfCxNdtU7C7EQ+SjPgCH
pIuSkSrB4o/HPt5/ocwVuQcWsPb8ZnwipPnVO0voUvG4sLRqqanarvMPHjE97NiA
EF7pdqqYS+Dnz1eCKWenPswrB3O7tWchv+X/YS+4ECutOaVBTW5HnZu5nwKBgE26
Au/wNQhjDmf2KSKHuvbwZhOiAzfcnnOXe/4e1K0snN0WepW4oQbiVIKq4fCBBOcb
yxCmr+nCE3VzrKVT8IaYcAteWYm4b4rO7QX52Y+1P6TGQeKaMXvRDj6/h1bcn0D7
RJ209GJbsDCqIz9H55CHPs9lFN7ZSbG0Z/fcDzWrAoGBAIJ3LRwHzxtf6URVBiTO
LX6KYDQ3sxLJxIvQnGzpnf5Tz3Hwz56CwcL+N/2vASNWUfNJRGk84rMpLYqHobIc
A7W9QZif5v7cVrWEqhgzuYj5U020+3/gTjqLTSpd92DfcxK391LuIYSxe3UFci01
mWOU/+psAlH/KAU7sATS0nJw
-----END PRIVATE KEY-----

27
activate/certs/key_1.pem Normal file
View File

@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAs/+OckN+WKJnhoDvNAoNUuxepIjW6EwpQraygWpXvfLCmigq
IlfoBu72Cjk5/+Q4Q5msBwE3tRDba5AJd3gI7xNLh6XEZ0otgdQXMLdK/u2Mq8aq
DAg4lwY2vIKc15+GmYgxGLXQ+Y1QKuzdsJFZxxHRsqhgcodzNKZlD1ATU0ys7+2B
lH0HhsxESpb9COwdOjkti8GDi2+l4O9CPQsaj66sHpURnG6xT+CMqzOguUK07ntX
zMt9LlxSMWEuUlo1srN9eK81pifs+5Zmpd3Boft6HO54ZcVZU/YFoHOldLdvpVWs
4Zgj0L11uuB4xKEL75j1c5DIb27fHQUFHFeXWwIDAQABAoIBAHpXOHpxZW7Y84R4
Hh1At82k2q76UxeQrHqrsqhDn197oMkmg3zUQuBzisdoHgPPGm7YBBS+vx5SVtDF
Faod6/STKMMJ6HRL9qJHhXuXGWIsE3CtluaWr9/2WT3HiNMFmnbAGp6w9Vios9R5
KjTYliy3789+og82ARfIAOhnhLsYPk3ZaoM8NCJLC9w79pde1sN3+hy2DsaSxU2t
lqP4BB+YDjAvzXCwVIuJzlM94Rjmc7SpHqSl62V4y0QKFzb31dwTaS8ZPznDCT+J
TTa6P23Rq9UyYThJ4mqyiR1v5WoixBETVJ0YCQyEeYN8Cl9trgRs7ehuVJP/n/np
2VjV0AECgYEA4ULUNKkP5I8awO2ZWECPpPD6/nX/BKcCgBkgB+xAlSj84Gl2ebkh
UEHsFx9aVqh+vjYg1rnKcvT4WtWHj6+Zg/L7+A7duQVL5wPO3R6Hv9AtN//UBmYw
CYL0omnK33i2Xm0KNoHP2qyc8NdeFG+4s6nmFCnDpu6t+FTCguKqas8CgYEAzI+J
hzzb5hpF2HiIM8CDj6EQ9NnljlwkQHHQ26+aeX7O9TktUdDsnQhDZBeMe5b5l6ry
TSIO7LvB2NknEn3ida4xPPBszorHCl3s+y8EaJPqEJpa3kJHLmkpLoYl0IBe9nBv
qNq+gNbO1EqO0WpJXvkJw19XFD+IsdqYGWHdfbUCgYAeEJhYCvR3Ee6FL3EXCU7i
udMWIVOPIGdCB5hx1kNsZRMTNKA+cMoUdyf2khWxmT2JT5okbpmprC51mShFsi8w
ID8nBXzsU9ukkqqKcqe1nyhZ4AhBbmQc8OCLyMjt+KOGiNEjI0JFsmIj9uq0mZCy
UgvGRyi44ARSL6XiaDRMmwKBgQCuljssnzaW9wlxrb2hXcdrSG5ua6JOlDmkfv9g
4EkZNK9x//C8CXJIvAq4XJjbyokPdykBx/Ww+raBWyTL1YD4K0l+1zwmM6+ZG3zt
DL3Ye5WjwfmYBD04USyCtp+DimyCLIhANyGsWKmB6nhmJy6jzi0VAEQT1f3Wvdhe
oXYmmQKBgHtq5nNOtCVQ4XsDUXYbs3lo2LnU/9ENZ6jSDlE878qFnBvEdaZYT/LB
Q9OOKOvJlHiaihRTb+aIR9QuikVgU7dsr33Yak4LDpOFgQpyi+y0ZImuo/3rR1xk
9earvT3iWLqjaMNQuV9IOHDtyYLTYSLE8M1Ag1noOE5O1Zh4NuUe
-----END RSA PRIVATE KEY-----

View File

@@ -0,0 +1,53 @@
package main
import (
"context"
"log"
"safeline/certs"
"safeline/safeline/proto/v1/server"
"google.golang.org/grpc"
)
var (
endpoint = "localhost:50052"
)
func main() {
creds, err := certs.LoadTransportCredentials()
if err != nil {
panic(err)
}
conn, err := grpc.Dial(endpoint, grpc.WithTransportCredentials(creds))
if err != nil {
panic(err)
}
defer conn.Close()
client := server.NewServerClient(conn)
c, err := client.Sync(context.TODO())
if err != nil {
panic(err)
}
req := &server.SyncRequest{}
req.Message = &server.SyncRequest_ValidateCode{
ValidateCode: &server.ValidateCode{
Code: "123",
MachineId: "456",
Version: "789",
},
}
if err := c.Send(req); err != nil {
panic(err)
}
resp, err := c.Recv()
if err != nil {
panic(err)
}
log.Printf("resp: %+v", resp)
}

109
activate/cmd/server/main.go Normal file
View File

@@ -0,0 +1,109 @@
package main
import (
"net"
"net/http"
"safeline/certs"
"safeline/safeline/proto/v1/server"
"time"
"go.uber.org/zap"
"google.golang.org/grpc"
)
type ServerImpl struct {
server.UnimplementedServerServer
}
func (s *ServerImpl) Sync(conn server.Server_SyncServer) error {
for {
req, err := conn.Recv()
if err != nil {
return err
}
zap.L().Info("recv", zap.Any("req", req))
res := &server.SyncPush{}
switch req.Message.(type) {
case *server.SyncRequest_Ping:
res.Message = &server.SyncPush_Pong{}
case *server.SyncRequest_ValidateCode:
res.Message = &server.SyncPush_ValidateResult{
ValidateResult: &server.ValidateResult{
State: server.ValidateCodeState_VALIDATE_CODE_STATE_SUCCESS,
ExpiredAt: time.Now().Add(time.Hour * 24 * 365 * 16).Unix(),
OrgName: "114514 Inc.",
OrgId: "114514",
Id: "114514",
Edition: server.Edition_EDITION_ENTERPRISE,
},
}
case *server.SyncRequest_ChangeCode:
res.Message = &server.SyncPush_ChangeCodeResult{
ChangeCodeResult: &server.ChangeCodeResult{
State: server.ValidateCodeState_VALIDATE_CODE_STATE_SUCCESS,
ExpiredAt: time.Now().Add(time.Hour * 24 * 365 * 16).Unix(),
OrgName: "114514 Inc.",
OrgId: "114514",
Id: "114514",
Edition: server.Edition_EDITION_ENTERPRISE,
},
}
case *server.SyncRequest_GivebackCode:
res.Message = &server.SyncPush_GivebackCodeResult{
GivebackCodeResult: &server.GiveBackCodeResult{
State: server.GiveBackCodeState_GIVEBACK_CODE_STATE_FAILED,
},
}
}
zap.L().Info("send", zap.Any("res", res))
if err := conn.Send(res); err != nil {
return err
}
}
}
func httpServer() *http.Server {
serveMux := http.NewServeMux()
// record all
serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
zap.L().Info(
"http",
zap.String("url", r.URL.String()),
zap.String("method", r.Method),
zap.Any("header", r.Header),
zap.Any("query", r.URL.Query()),
)
})
return &http.Server{
Addr: ":50052",
Handler: serveMux,
}
}
func main() {
logger, _ := zap.NewDevelopment()
zap.ReplaceGlobals(logger)
tlsCredentials, err := certs.LoadTransportCredentials()
if err != nil {
panic(err)
}
grpcServer := grpc.NewServer(
grpc.Creds(tlsCredentials),
)
server.RegisterServerServer(grpcServer, &ServerImpl{})
l, err := net.Listen("tcp", ":50052")
if err != nil {
panic(err)
}
zap.L().Info("server started")
grpcServer.Serve(l)
}

20
activate/go.mod Normal file
View File

@@ -0,0 +1,20 @@
module safeline
go 1.22
toolchain go1.23.4
require (
go.uber.org/zap v1.27.0
google.golang.org/grpc v1.69.4
google.golang.org/protobuf v1.36.2
)
require (
github.com/golang/protobuf v1.5.4 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 // indirect
)

53
activate/go.sum Normal file
View File

@@ -0,0 +1,53 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo=
go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe h1:bQnxqljG/wqi4NTXu2+DJ3n7APcEA882QZ1JvhQAq9o=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422 h1:3UsHvIr4Wc2aW4brOaSCmcxh9ksica6fHEr8P1XhkYw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250106144421-5f5ef82da422/go.mod h1:3ENsm/5D1mzDyhpzeRi1NR784I0BcofWBoSc5QqqMK4=
google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0=
google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs=
google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.36.2 h1:R8FeyR1/eLmkutZOM5CWghmo5itiG9z0ktFlTVLuTmU=
google.golang.org/protobuf v1.36.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -0,0 +1,77 @@
syntax = "proto3";
package chaitin.safeline_ops.proto.v1.server;
option go_package = "safeline/proto/v1/server";
import "google/protobuf/empty.proto";
service Server {
rpc Sync(stream SyncRequest) returns (stream SyncPush);
}
message ValidateCode {
string code = 1;
string machine_id = 2;
string version = 3;
}
message ChangeCode {
string new_code = 1;
string machine_id = 2;
string version = 3;
}
message GiveBackCode {
string code = 1;
string machine_id = 2;
string version = 3;
}
message ValidateResult {
ValidateCodeState state = 1;
int64 expired_at = 2;
string org_name = 3;
string org_id = 4;
}
message ChangeCodeResult {
ValidateCodeState state = 1;
int64 expired_at = 2;
string org_name = 3;
string org_id = 4;
}
message GiveBackCodeResult {
GiveBackCodeState state = 1;
}
message SyncRequest {
oneof message {
google.protobuf.Empty ping = 1;
ValidateCode validate_code = 2;
ChangeCode change_code = 3;
GiveBackCode giveback_code = 4;
}
}
message SyncPush {
oneof message {
google.protobuf.Empty pong = 1;
ValidateResult validate_result = 2;
ChangeCodeResult change_code_result = 3;
GiveBackCodeResult giveback_code_result = 4;
}
}
enum ValidateCodeState {
VALIDATE_CODE_STATE_SUCCESS = 0;
VALIDATE_CODE_STATE_EXPIRED = 1;
VALIDATE_CODE_STATE_INVALID = 2;
VALIDATE_CODE_STATE_USED = 3;
VALIDATE_CODE_STATE_CONNECTED = 4;
}
enum GiveBackCodeState {
GIVEBACK_CODE_STATE_SUCCESS = 0;
GIVEBACK_CODE_STATE_FAILED = 1;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,154 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.5.1
// - protoc v5.29.1
// source: proto_server.proto
package server
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.64.0 or later.
const _ = grpc.SupportPackageIsVersion9
const (
Server_Sync_FullMethodName = "/chaitin.safeline_ops.proto.v1.server.Server/Sync"
Server_IPInfo_FullMethodName = "/chaitin.safeline_ops.proto.v1.server.Server/IPInfo"
)
// ServerClient is the client API for Server service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type ServerClient interface {
Sync(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SyncRequest, SyncPush], error)
IPInfo(ctx context.Context, in *IPInfoRequest, opts ...grpc.CallOption) (*IPInfoResponse, error)
}
type serverClient struct {
cc grpc.ClientConnInterface
}
func NewServerClient(cc grpc.ClientConnInterface) ServerClient {
return &serverClient{cc}
}
func (c *serverClient) Sync(ctx context.Context, opts ...grpc.CallOption) (grpc.BidiStreamingClient[SyncRequest, SyncPush], error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
stream, err := c.cc.NewStream(ctx, &Server_ServiceDesc.Streams[0], Server_Sync_FullMethodName, cOpts...)
if err != nil {
return nil, err
}
x := &grpc.GenericClientStream[SyncRequest, SyncPush]{ClientStream: stream}
return x, nil
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Server_SyncClient = grpc.BidiStreamingClient[SyncRequest, SyncPush]
func (c *serverClient) IPInfo(ctx context.Context, in *IPInfoRequest, opts ...grpc.CallOption) (*IPInfoResponse, error) {
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
out := new(IPInfoResponse)
err := c.cc.Invoke(ctx, Server_IPInfo_FullMethodName, in, out, cOpts...)
if err != nil {
return nil, err
}
return out, nil
}
// ServerServer is the server API for Server service.
// All implementations must embed UnimplementedServerServer
// for forward compatibility.
type ServerServer interface {
Sync(grpc.BidiStreamingServer[SyncRequest, SyncPush]) error
IPInfo(context.Context, *IPInfoRequest) (*IPInfoResponse, error)
mustEmbedUnimplementedServerServer()
}
// UnimplementedServerServer must be embedded to have
// forward compatible implementations.
//
// NOTE: this should be embedded by value instead of pointer to avoid a nil
// pointer dereference when methods are called.
type UnimplementedServerServer struct{}
func (UnimplementedServerServer) Sync(grpc.BidiStreamingServer[SyncRequest, SyncPush]) error {
return status.Errorf(codes.Unimplemented, "method Sync not implemented")
}
func (UnimplementedServerServer) IPInfo(context.Context, *IPInfoRequest) (*IPInfoResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method IPInfo not implemented")
}
func (UnimplementedServerServer) mustEmbedUnimplementedServerServer() {}
func (UnimplementedServerServer) testEmbeddedByValue() {}
// UnsafeServerServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ServerServer will
// result in compilation errors.
type UnsafeServerServer interface {
mustEmbedUnimplementedServerServer()
}
func RegisterServerServer(s grpc.ServiceRegistrar, srv ServerServer) {
// If the following call pancis, it indicates UnimplementedServerServer was
// embedded by pointer and is nil. This will cause panics if an
// unimplemented method is ever invoked, so we test this at initialization
// time to prevent it from happening at runtime later due to I/O.
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
t.testEmbeddedByValue()
}
s.RegisterService(&Server_ServiceDesc, srv)
}
func _Server_Sync_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(ServerServer).Sync(&grpc.GenericServerStream[SyncRequest, SyncPush]{ServerStream: stream})
}
// This type alias is provided for backwards compatibility with existing code that references the prior non-generic stream type by name.
type Server_SyncServer = grpc.BidiStreamingServer[SyncRequest, SyncPush]
func _Server_IPInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(IPInfoRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ServerServer).IPInfo(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: Server_IPInfo_FullMethodName,
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ServerServer).IPInfo(ctx, req.(*IPInfoRequest))
}
return interceptor(ctx, in, info, handler)
}
// Server_ServiceDesc is the grpc.ServiceDesc for Server service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Server_ServiceDesc = grpc.ServiceDesc{
ServiceName: "chaitin.safeline_ops.proto.v1.server.Server",
HandlerType: (*ServerServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "IPInfo",
Handler: _Server_IPInfo_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "Sync",
Handler: _Server_Sync_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "proto_server.proto",
}

241
extract/descpb_to_proto.py Normal file
View File

@@ -0,0 +1,241 @@
#!/usr/bin/python3
#-*- encoding: Utf-8 -*-
from google.protobuf.descriptor_pb2 import DescriptorProto, FieldDescriptorProto
from collections import OrderedDict
from itertools import groupby
"""
This script converts back a FileDescriptor structure to a readable .proto file.
There is already a function in the standard C++ library that does this [1], but
- It is not accessible through the Python binding
- This implementation has a few output readability improvements, i.e
-- Declaring enums/messages after first use rather than at top of block
-- Not always using full names when referencing messages types
-- Smaller aesthetic differences (number of tabs, line jumps)
For reference of the FileDescriptor structure, see [2].
Other (less complete) implementations of this are [3] or [4].
[1] https://github.com/google/protobuf/blob/5a76e/src/google/protobuf/descriptor.cc#L2242
[2] https://github.com/google/protobuf/blob/bb77c/src/google/protobuf/descriptor.proto#L59
[3] https://github.com/fry/d3/blob/master/decompile_protobins.py
[4] https://github.com/sarum9in/bunsan_binlogs_python/blob/master/src/python/source.py
"""
INDENT = ' ' * 4
def descpb_to_proto(desc):
out = 'syntax = "%s";\n\n' % (desc.syntax or 'proto2')
scopes = ['']
if desc.package:
out += 'package %s;\n\n' % desc.package
scopes[0] += '.' + desc.package
for index, dep in enumerate(desc.dependency):
prefix = ' public' * (index in desc.public_dependency)
prefix += ' weak' * (index in desc.weak_dependency)
out += 'import%s "%s";\n' % (prefix, dep)
scopes.append('.' + ('/' + dep.rsplit('/', 1)[0])[1:].replace('/', '.'))
out += '\n' * (out[-2] != '\n')
out += parse_msg(desc, scopes, desc.syntax).strip('\n')
name = desc.name.replace('..', '').strip('.\\/')
return name, out + '\n'
def parse_msg(desc, scopes, syntax):
out = ''
is_msg = isinstance(desc, DescriptorProto)
if is_msg:
scopes = list(scopes)
scopes[0] += '.' + desc.name
blocks = OrderedDict()
for nested_msg in (desc.nested_type if is_msg else desc.message_type):
blocks[nested_msg.name] = parse_msg(nested_msg, scopes, syntax)
for enum in desc.enum_type:
out2 = ''
for val in enum.value:
out2 += '%s = %s;\n' % (val.name, fmt_value(val.number, val.options))
if len(set(i.number for i in enum.value)) == len(enum.value):
enum.options.ClearField('allow_alias')
blocks[enum.name] = wrap_block('enum', out2, enum)
if is_msg and desc.options.map_entry:
return ' map<%s>' % ', '.join(min_name(i.type_name, scopes) \
if i.type_name else types[i.type] \
for i in desc.field)
if is_msg:
for field in desc.field:
out += fmt_field(field, scopes, blocks, syntax)
for index, oneof in enumerate(desc.oneof_decl):
out += wrap_block('oneof', blocks.pop('_oneof_%d' % index), oneof)
out += fmt_ranges('extensions', desc.extension_range)
out += fmt_ranges('reserved', [*desc.reserved_range, *desc.reserved_name])
else:
for service in desc.service:
out2 = ''
for method in service.method:
out2 += 'rpc %s(%s%s) returns (%s%s);\n' % (method.name,
'stream ' * method.client_streaming,
min_name(method.input_type, scopes),
'stream ' * method.server_streaming,
min_name(method.output_type, scopes))
out += wrap_block('service', out2, service)
extendees = OrderedDict()
for ext in desc.extension:
extendees.setdefault(ext.extendee, '')
extendees[ext.extendee] += fmt_field(ext, scopes, blocks, syntax, True)
for name, value in blocks.items():
out += value[:-1]
for name, fields in extendees.items():
out += wrap_block('extend', fields, name=min_name(name, scopes))
out = wrap_block('message' * is_msg, out, desc)
return out
def fmt_value(val, options=None, desc=None, optarr=[]):
if type(val) != str:
if type(val) == bool:
val = str(val).lower()
elif desc and desc.enum_type:
val = desc.enum_type.values_by_number[val].name
val = str(val)
else:
val = '"%s"' % val.encode('unicode_escape').decode('utf8')
if options:
opts = [*optarr]
for (option, value) in options.ListFields():
opts.append('%s = %s' % (option.name, fmt_value(value, desc=option)))
if opts:
val += ' [%s]' % ', '.join(opts)
return val
types = {v: k.split('_')[1].lower() for k, v in FieldDescriptorProto.Type.items()}
labels = {v: k.split('_')[1].lower() for k, v in FieldDescriptorProto.Label.items()}
def fmt_field(field, scopes, blocks, syntax, extend=False):
type_ = types[field.type]
default = ''
if field.default_value:
if field.type == field.TYPE_STRING:
default = ['default = %s' % fmt_value(field.default_value)]
elif field.type == field.TYPE_BYTES:
default = ['default = "%s"' % field.default_value]
else:
# Guess whether it ought to be more readable as base 10 or 16,
# based on the presence of repeated digits:
if ('int' in type_ or 'fixed' in type_) and \
int(field.default_value) >= 0x10000 and \
not any(len(list(i)) > 3 for _, i in groupby(str(field.default_value))):
field.default_value = hex(int(field.default_value))
default = ['default = %s' % field.default_value]
out = ''
if field.type_name:
type_ = min_name(field.type_name, scopes)
short_type = type_.split('.')[-1]
if short_type in blocks and ((not extend and not field.HasField('oneof_index')) or \
blocks[short_type].startswith(' map<')):
out += blocks.pop(short_type)[1:]
if out.startswith('map<'):
line = out + ' %s = %s;\n' % (field.name, fmt_value(field.number, field.options, optarr=default))
out = ''
elif field.type != field.TYPE_GROUP:
line = '%s %s %s = %s;\n' % (labels[field.label], type_, field.name, fmt_value(field.number, field.options, optarr=default))
else:
line = '%s group %s = %d ' % (labels[field.label], type_, field.number)
out = out.split(' ', 2)[-1]
if field.HasField('oneof_index') or (syntax == 'proto3' and line.startswith('optional')):
line = line.split(' ', 1)[-1]
if out:
line = '\n' + line
if field.HasField('oneof_index'):
blocks.setdefault('_oneof_%d' % field.oneof_index, '')
blocks['_oneof_%d' % field.oneof_index] += line + out
return ''
else:
return line + out
"""
Find the smallest name to refer to another message from our scopes.
For this, we take the final part of its name, and expand it until
the path both scopes don't have in common (if any) is specified; and
expand it again if there are multiple outer packages/messages in the
scopes sharing the same name, and that the first part of the obtained
partial name is one of them, leading to ambiguity.
"""
def min_name(name, scopes):
name, cur_scope = name.split('.'), scopes[0].split('.')
short_name = [name.pop()]
while name and (cur_scope[:len(name)] != name or \
any(list_rfind(scope.split('.'), short_name[0]) > len(name) \
for scope in scopes)):
short_name.insert(0, name.pop())
return '.'.join(short_name)
def wrap_block(type_, value, desc=None, name=None):
out = ''
if type_:
out = '\n%s %s {\n' % (type_, name or desc.name)
if desc:
for (option, optval) in desc.options.ListFields():
value = 'option %s = %s;\n' % (option.name, fmt_value(optval, desc=option)) + value
value = value.replace('\n\n\n', '\n\n')
if type_:
out += '\n'.join(INDENT + line for line in value.strip('\n').split('\n'))
out += '\n}\n\n'
else:
out += value
return out
def fmt_ranges(name, ranges):
text = []
for range_ in ranges:
if type(range_) != str and range_.end - 1 > range_.start:
if range_.end < 0x20000000:
text.append('%d to %d' % (range_.start, range_.end - 1))
else:
text.append('%d to max' % range_.start)
elif type(range_) != str:
text.append(fmt_value(range_.start))
else:
text.append(fmt_value(range_))
if text:
return '\n%s %s;\n' % (name, ', '.join(text))
return ''
# Fulfilling a blatant lack of the Python language.
list_rfind = lambda x, i: len(x) - 1 - x[::-1].index(i) if i in x else -1

74
extract/grpc.py Normal file
View File

@@ -0,0 +1,74 @@
#!/usr/bin/python3
# -*- encoding: Utf-8 -*-
from pathlib import Path
from google.protobuf.descriptor_pb2 import FileDescriptorProto
from google.protobuf.internal.decoder import _DecodeVarint
from extract.descpb_to_proto import descpb_to_proto
def walk_binary(binr):
with open(binr, "rb") as fd:
binr = fd.read()
# Search for:
# ".proto" or ".protodevel", as part of the "name" (1) field
cursor = 0
while cursor < len(binr):
cursor = binr.find(b".proto", cursor)
if cursor == -1:
break
cursor += len(".proto")
cursor += (binr[cursor : cursor + 5] == b"devel") * 5
# Search back for the (1, length-delimited) marker
start = binr.rfind(b"\x0a", max(cursor - 1024, 0), cursor)
if start > 0 and binr[start - 1] == 0x0A == (cursor - start - 1):
start -= 1
# Check whether length byte is coherent
if start == -1:
continue
varint, end = _DecodeVarint(binr, start + 1)
if cursor - end != varint:
continue
# Look just after for subsequent markers
tags = b"\x12\x1a\x22\x2a\x32\x3a\x42\x4a\x50\x58\x62"
if binr[cursor] not in tags:
continue
while cursor < len(binr) and binr[cursor] in tags:
tags = tags[tags.index(binr[cursor]) :]
varint, end = _DecodeVarint(binr, cursor + 1)
cursor = end + varint * (binr[cursor] & 0b111 == 2)
# Parse descriptor
proto = FileDescriptorProto()
proto.ParseFromString(binr[start:cursor])
# Convert to ascii
yield descpb_to_proto(proto)
if __name__ == "__main__":
base = Path(__file__).resolve().parent
proto_base = base / "proto"
proto_base.mkdir(exist_ok=True)
for name, proto in walk_binary("./mgt"):
if (
name.startswith("google")
or name.startswith("grpc")
or name.startswith("github.com/golang/protobuf")
):
continue
p = proto_base / name
p.parent.mkdir(parents=True, exist_ok=True)
print(p)
with open(p, "w") as fd:
fd.write(proto)

63
extract/proto/fvm.proto Normal file
View File

@@ -0,0 +1,63 @@
syntax = "proto3";
package safeline_fvm;
option go_package = "proto/fvm;safeline_fvm";
service FVM {
rpc AppendFSL(stream Text) returns (Status);
rpc GetVersion(Empty) returns (VersionInfo);
rpc GetBytecode(Version) returns (stream Chunk);
rpc GetFSL(Version) returns (stream Chunk);
rpc GetTableSVG(Empty) returns (stream Chunk);
rpc CompileFSL(FslText) returns (Status);
rpc GetPartFSL(FslInfo) returns (FslText);
rpc GetPartDot(FslInfo) returns (stream Chunk);
rpc UpdateState(StateText) returns (StateText);
}
message StateText {
string Text = 1;
}
message FslInfo {
string TaName = 1;
string Id = 2;
}
message FslText {
string Text = 1;
}
message Text {
bool Incremental = 1;
string Text = 3;
}
message Status {
bool Ok = 1;
string Err = 2;
int64 Version = 3;
}
message Empty {
}
message VersionInfo {
int64 Latest = 1;
int64 Oldest = 2;
}
message Version {
int64 From = 1;
}
message Chunk {
bool Ok = 1;
string Err = 2;
int64 Version = 3;
bytes Payload = 4;
int64 NumChunks = 5;
int64 ChunkIndex = 6;
}

View File

@@ -0,0 +1,76 @@
syntax = "proto3";
import "google/protobuf/empty.proto";
option go_package = "proto/sync";
service SyncService {
rpc Sync(stream SyncRequest) returns (stream SyncTask);
}
message SyncRequest {
oneof message {
google.protobuf.Empty ping = 1;
TableStatus table_status = 2;
TaskResult task_result = 3;
int64 fetch_static = 4;
ClientStatus client_status = 5;
google.protobuf.Empty uninstall = 6;
}
}
message ClientStatus {
bool nginx_health = 2;
}
message TableStatus {
string name = 1;
int64 last_sync = 2;
string hash = 3;
}
message TaskResult {
string task_id = 1;
int32 status = 2;
string error = 3;
}
message SyncTask {
oneof message {
MasterStatus pong = 1;
TableTask table = 2;
FileTask file = 3;
google.protobuf.Empty uninstall = 4;
google.protobuf.Empty reset = 5;
ClientSync clientSync = 6;
}
}
message MasterStatus {
string version = 1;
bool nginx_health = 2;
}
message ClientSync {
int64 last_sync = 1;
bool sync = 2;
string master_ver = 3;
}
message TableTask {
string task_id = 1;
string task_name = 2;
string table_name = 3;
int64 from = 4;
int64 to = 5;
bytes data = 6;
bool full = 7;
}
message FileTask {
string task_id = 1;
string task_name = 2;
string file_name = 3;
int64 site_id = 4;
bytes data = 5;
}

View File

@@ -0,0 +1,30 @@
syntax = "proto3";
package chaitin.safeline_ops.proto.v1.bridge;
import "google/protobuf/empty.proto";
service Bridge {
rpc Metadata(google.protobuf.Empty) returns (MetadataResp);
rpc LicenseCtrl(LicenseCtrlReq) returns (google.protobuf.Empty);
}
message MetadataResp {
string version = 1;
string machine_id = 2;
LicenseKind license_kind = 3;
}
message LicenseCtrlReq {
oneof action {
string apply = 1;
google.protobuf.Empty remove = 2;
}
}
enum LicenseKind {
LICENSE_KIND_UNSPECIFIED = 0;
LICENSE_KIND_COMMUNITY = 1;
LICENSE_KIND_PRO = 2;
LICENSE_KIND_ENTERPRISE = 3;
}

View File

@@ -0,0 +1,110 @@
syntax = "proto3";
package chaitin.safeline_ops.proto.v1.server;
import "google/protobuf/empty.proto";
service Server {
rpc Sync(stream SyncRequest) returns (stream SyncPush);
rpc IPInfo(IPInfoRequest) returns (IPInfoResponse);
}
message ValidateCode {
string code = 1;
string machine_id = 2;
string version = 3;
string id = 4;
}
message ChangeCode {
string new_code = 1;
string machine_id = 2;
string version = 3;
string id = 4;
}
message GiveBackCode {
string code = 1;
string machine_id = 2;
string version = 3;
string id = 4;
}
message ValidateResult {
ValidateCodeState state = 1;
int64 expired_at = 2;
string org_name = 3;
string org_id = 4;
string id = 5;
Edition edition = 6;
}
message ChangeCodeResult {
ValidateCodeState state = 1;
int64 expired_at = 2;
string org_name = 3;
string org_id = 4;
string id = 5;
Edition edition = 6;
}
message GiveBackCodeResult {
GiveBackCodeState state = 1;
string id = 2;
}
message SyncRequest {
oneof message {
google.protobuf.Empty ping = 1;
ValidateCode validate_code = 2;
ChangeCode change_code = 3;
GiveBackCode giveback_code = 4;
}
}
message SyncPush {
oneof message {
google.protobuf.Empty pong = 1;
ValidateResult validate_result = 2;
ChangeCodeResult change_code_result = 3;
GiveBackCodeResult giveback_code_result = 4;
}
}
message IPInfoRequest {
}
message IPInfoResponse {
string ip = 1;
string country = 2;
string province = 3;
string city = 4;
string areacode = 5;
string timezone = 6;
string zipcode = 7;
string lngwgs = 8;
string latwgs = 9;
string isp = 10;
string owner = 11;
string adcode = 12;
}
enum ValidateCodeState {
VALIDATE_CODE_STATE_SUCCESS = 0;
VALIDATE_CODE_STATE_EXPIRED = 1;
VALIDATE_CODE_STATE_INVALID = 2;
VALIDATE_CODE_STATE_USED = 3;
VALIDATE_CODE_STATE_CONNECTED = 4;
}
enum Edition {
EDITION_UNKOWN = 0;
EDITION_PROFESSIONAL = 1;
EDITION_ENTERPRISE = 2;
}
enum GiveBackCodeState {
GIVEBACK_CODE_STATE_SUCCESS = 0;
GIVEBACK_CODE_STATE_FAILED = 1;
}

12
pyproject.toml Normal file
View File

@@ -0,0 +1,12 @@
[project]
name = "safeline"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.11.3"
dependencies = []
[dependency-groups]
dev = [
"protobuf>=6.30.0",
]

16
replace.py Normal file
View File

@@ -0,0 +1,16 @@
from sys import argv
public_key = b"""safeline-cloud.chaitin.com:50052"""
new_public_key = b"""crack-domain-length-is--26:50052"""
with open(argv[1], "rb") as f:
data = f.read()
assert public_key in data
assert len(public_key) == len(new_public_key)
with open(argv[1], "wb") as f:
f.write(data.replace(public_key, new_public_key))

31
uv.lock generated Normal file
View File

@@ -0,0 +1,31 @@
version = 1
requires-python = ">=3.11.3"
[[package]]
name = "protobuf"
version = "6.30.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/53/6a/2629bb3529e5bdfbd6c4608ff5c7d942cd4beae85793f84ba543aab2548a/protobuf-6.30.0.tar.gz", hash = "sha256:852b675d276a7d028f660da075af1841c768618f76b90af771a8e2c29e6f5965", size = 429239 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/aa/76/8b1cbf762d98b09fcb29bbc6eca97dc6e1cd865b97a49c443aa23f1a9f82/protobuf-6.30.0-cp310-abi3-win32.whl", hash = "sha256:7337d76d8efe65ee09ee566b47b5914c517190196f414e5418fa236dfd1aed3e", size = 419141 },
{ url = "https://files.pythonhosted.org/packages/57/50/2ea2fb4533321438f5106723c70c303529ba184540e619ebe75e790d402e/protobuf-6.30.0-cp310-abi3-win_amd64.whl", hash = "sha256:9b33d51cc95a7ec4f407004c8b744330b6911a37a782e2629c67e1e8ac41318f", size = 430995 },
{ url = "https://files.pythonhosted.org/packages/a1/7d/a7dfa7aa3deda114920b1ed57c0026e85a976e74658db2784a0443510252/protobuf-6.30.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:52d4bb6fe76005860e1d0b8bfa126f5c97c19cc82704961f60718f50be16942d", size = 417570 },
{ url = "https://files.pythonhosted.org/packages/11/87/a9c7b020c4072dc34e3a2a3cde69366ffc623afff0e7f10f4e5275aaec01/protobuf-6.30.0-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:7940ab4dfd60d514b2e1d3161549ea7aed5be37d53bafde16001ac470a3e202b", size = 317310 },
{ url = "https://files.pythonhosted.org/packages/95/66/424db2262723781dc94208ff9ce201df2f44f18a46fbff3c067812c6b5b9/protobuf-6.30.0-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:d79bf6a202a536b192b7e8d295d7eece0c86fbd9b583d147faf8cfeff46bf598", size = 316203 },
{ url = "https://files.pythonhosted.org/packages/51/6f/21c2b7de96c3051f847a4a88a12fdf015ed6b7d50fc131fb101a739bd7a5/protobuf-6.30.0-py3-none-any.whl", hash = "sha256:e5ef216ea061b262b8994cb6b7d6637a4fb27b3fb4d8e216a6040c0b93bd10d7", size = 167054 },
]
[[package]]
name = "safeline"
version = "0.1.0"
source = { virtual = "." }
[package.dev-dependencies]
dev = [
{ name = "protobuf" },
]
[package.metadata]
[package.metadata.requires-dev]
dev = [{ name = "protobuf", specifier = ">=6.30.0" }]